【问题标题】:ternary operator of different types不同类型的三元运算符
【发布时间】:2015-04-01 01:01:59
【问题描述】:

以下代码在 g++ 4.9.2 和 clang++ 3.7.0 下的行为不同。哪一个是正确的?标准中的哪些部分与此相关?谢谢。

#include <iostream>
using namespace std;

struct Base {
  Base() = default;
  Base(const Base&) = default;
  Base(Base&&) = delete;
};

struct Derived : Base {
};

int main() {
  const Base& b = true ? Derived() : Base();
}

g++ 接受它并且 clang++ 给出错误incompatible operand types ('Derived' and 'Base')。详情见下文。

[hidden]$ g++ -v
Using built-in specs.
COLLECT_GCC=/usr/bin/g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-redhat-linux/4.9.2/lto-wrapper
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads=posix --enable-checking=release --enable-multilib --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-gnu-unique-object --enable-linker-build-id --with-linker-hash-style=gnu --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto --enable-plugin --enable-initfini-array --disable-libgcj --with-isl=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/isl-install --with-cloog=/builddir/build/BUILD/gcc-4.9.2-20150212/obj-x86_64-redhat-linux/cloog-install --enable-gnu-indirect-function --with-tune=generic --with-arch_32=i686 --build=x86_64-redhat-linux
Thread model: posix
gcc version 4.9.2 20150212 (Red Hat 4.9.2-6) (GCC) 
[hidden]$ g++ -std=c++11 b.cpp 
[hidden]$ clang++ -v
clang version 3.7.0 (http://llvm.org/git/clang.git 6bbdbba8ec8a7730c68fee94363547dc2dc65b10)
Target: x86_64-unknown-linux-gnu
Thread model: posix
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/3.4.6
Found candidate GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.9.2
Selected GCC installation: /usr/lib/gcc/x86_64-redhat-linux/4.9.2
Candidate multilib: .;@m64
Candidate multilib: 32;@m32
Selected multilib: .;@m64
[hidden]$ clang++ -std=c++11 b.cpp 
b.cpp:14:24: error: incompatible operand types ('Derived' and 'Base')
  const Base& b = true ? Derived() : Base();
                       ^ ~~~~~~~~~   ~~~~~~
1 error generated.

【问题讨论】:

  • Clang 可能希望您使用显式转换
  • @dyp:克里斯没有说复制初始化意味着使用复制构造函数,这是错误的:如果有移动构造函数,则可能不需要复制构造函数。他说既然有拷贝构造函数,拷贝初始化应该成功,这是真的,要么有可用的移动构造函数,要么没有,拷贝构造函数被使用。
  • @BenVoigt,没关系,当它说复制初始化时,我真的没有足够注意。进一步看,应该归结为Base b = Derived();的重载决议选择了哪个构造函数
  • @dyp,也许值得让 Clang 家伙知道这种情况下的异常错误消息?我承认这是相当做作的。
  • 切向问题:有什么办法可以做 OP 试图做的事情吗? (即根据条件将引用直接绑定到Base()Derived(),并延长临时的生命周期?

标签: c++ c++11 c++14


【解决方案1】:

我手边没有 N3936,但 N3797 §5.12 [expr.cond]/3 包含这个(强调我的):

否则,如果第二个和第三个操作数的类型不同,并且 要么具有(可能是 cv 限定的)类类型,要么两者都是 glvalues 具有相同的价值类别和相同的类型,除了 cv-qualification,尝试转换每个操作数 到对方的类型。确定是否存在的过程 T1 类型的操作数表达式 E1 可以转换为匹配操作数 T2 类型的表达式 E2 定义如下:

  • 如果 E2 是左值:[已删除]
  • 如果 E2 是一个 xvalue:[已删除]
  • 如果 E2 是纯右值,或者两个转换都不是 以上可以做到,并且至少有一个操作数有(可能 cv-qualified) 类类型:
    • 如果 E1 和 E2 具有类类型,并且 基础类类型相同或一个是 其他
      如果 T2 的类相同,则 E1 可以转换为匹配 E2 类型为,的基类,T1 的类,以及 cv 限定 T2 的 cv 资格与或更高的 cv 资格相同 比,T1 的 cv 资格。如果应用转换,E1 是 通过复制初始化临时的 从 E1 中键入 T2 并将该临时值用作转换后的操作数。

利用这个过程,判断第二个操作数是否可以 转换为匹配第三个操作数,以及第三个操作数是否 可以转换为匹配第二个操作数。如果两者都可以 已转换,或者可以转换但转换不明确, 该程序格式不正确。如果两者都不能转换,则操作数 保持不变,并按照所述执行进一步检查 以下。 如果只有一次转换是可能的,那么该转换是 应用于所选操作数,转换后的操作数用于 本节其余部分的原始操作数的位置。

现在要从 Derived() 复制初始化最终的 Base 操作数,我们可以查看 §13.3.1.3 [over.match.ctor]:

当类类型的对象被直接初始化时(8.5),或者 从相同或派生类的表达式复制初始化 type (8.5),重载决议选择构造函数。为了 直接初始化,候选函数都是 正在初始化的对象的类的构造函数。 为 复制初始化,候选函数都是转换 该类的构造函数(12.3.1)。参数列表是 初始化器的表达式列表或赋值表达式。

转换构造函数在 §12.3.1 [class.conv.ctor] 中定义如下:

没有显式函数说明符声明的构造函数 指定从其参数类型到类型的转换 它的类。这样的构造函数称为转换构造函数。

现在,如果您相信我(为了避免引用超过我对 13.3 的引用),prvalue Derived() 将导致重载决议选择移动构造函数(采用 Base&amp;&amp;),@ 987654321@,这会导致 Clang 的错误。

总之,Clang 发出错误是正确的。由于使用已删除的函数需要诊断,这是 GCC 中的一个错误。

【讨论】:

  • Clang 确实在尝试移动它:coliru.stacked-crooked.com/a/6542febd5d884ecc
  • 我很困惑,为什么这里会涉及到移动构造函数?它试图移动什么,它试图移动到哪里?
  • @dyp,那是我的错。让我回去看看。
  • @chris,从您的回答中仍然不清楚-也许您的回答暗示了这一点,但我宁愿更明确一些-程序是否格式错误以及 gcc 和叮当是正确的。
  • 文本似乎是说Derived()切片 以创建一个新的Base(),如果该项目被选中,则该Base() 将绑定到引用。这是一个令人惊讶的行为。
猜你喜欢
  • 2021-10-02
  • 1970-01-01
  • 2011-12-17
  • 2021-12-18
  • 2017-01-20
  • 2017-08-24
  • 2016-01-24
  • 1970-01-01
相关资源
最近更新 更多