不同类型的三元运算符

不同类型的三元运算符

问题描述:

下面的一段代码在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. 
+0

Clang可能希望你使用明确的转换 – fileoffset 2015-04-01 01:09:21

+0

@dyp:Chris没有说复制初始化意味着使用复制构造函数,该复制构造函数是false:如果存在移动构造函数,则可能不需要复制构造函数。他说由于有一个拷贝构造函数,所以拷贝初始化应该成功,这是真的,要么有一个可用的移动构造函数,要么没有拷贝构造函数被使用。 – 2015-04-01 01:32:23

+0

@BenVoigt,没关系,当它说复制初始化时,我真的没有足够的注意力。进一步看,它应该归结为哪个构造函数是通过重载分辨率挑选出来的?Base b = Derived();' – chris 2015-04-01 01:37:55

我没有N3936方便,但N3797§5.12[expr.cond/3包含此(重点煤矿):

否则,如果第二个和第三个操作数有不同的类型和 或者具有(可能是CV-合格的)类别类型,或者如果两者都是具有相同值类别的glvalues 和除了 cv-qualification之外的相同类型,则尝试将这些操作数 中的每一个转换为另一个。用于确定类型T1的 操作数表达E1是否可以转换,以匹配类型T2的操作数 表达E2的定义如下的方法:

  • 如果E2是一个左值:[移除]
  • 如果E2是一个x值:[移除]
  • 如果E2是prvalue或者如果操作数既不转化 以上可以做到和中的至少一个具有(可能 CV修饰)类类型:
    • 如果E1和E2具有类类型和 底层类类型相同或一个是基类 其他的:
      E1可以被转换以匹配E2如果类T2是相同的 类型如T1,的基类,T1的类别和T2的cv资格 与T1的cv限定相同,或更大的cv资格 。如果应用了转换,则E1将 更改为类型T2的前值复制初始化从E1中暂时变为 类型T2并将该临时值用作转换后的操作数。

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

我们了解Derived()复制初始化最后Base操作,大家可以看一下§13.3.1.3[over.match.ctor]:

当类的对象是直接初始化(8.5)或 从相同或派生类的表达式复制初始化 类型(8.5),重载分辨率选择构造函数。对于 直接初始化,候选函数都是被初始化的对象类的所有构造函数。 对于 复制初始化,候选函数全部转换为的构造函数 构造函数(12.3.1)。参数列表是初始值设定项的 表达式列表或赋值表达式。

转换构造被定义为在§12.3.1[class.conv.ctor]如下:

构造函数声明无功能说明符明确 指定从类型及其参数的转换其类别为 。这样的构造函数被称为转换构造函数。

现在,如果你相信我(对没有报价比我有13.3的缘故),一个prvalue Derived()会导致重载决议选择移动构造函数(以Base&&despite being deleted,这会导致Clang发生错误。

总之,Clang在发布错误时是正确的。由于使用删除的功能需要诊断,这是GCC中的一个错误。

+0

Clang确实试图移动它:http://coliru.stacked-crooked.com/a/6542febd5d884ecc – Blob 2015-04-01 01:25:20

+0

我很困惑,为什么移动构造函数在这里涉及?它试图移动什么,它试图移动到什么地方? – 2015-04-01 01:27:41

+0

@dyp,这将是我的不好。让我回去看看。 – chris 2015-04-01 01:28:17