不同类型的三元运算符
下面的一段代码在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.
我没有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中的一个错误。
Clang可能希望你使用明确的转换 – fileoffset 2015-04-01 01:09:21
@dyp:Chris没有说复制初始化意味着使用复制构造函数,该复制构造函数是false:如果存在移动构造函数,则可能不需要复制构造函数。他说由于有一个拷贝构造函数,所以拷贝初始化应该成功,这是真的,要么有一个可用的移动构造函数,要么没有拷贝构造函数被使用。 – 2015-04-01 01:32:23
@BenVoigt,没关系,当它说复制初始化时,我真的没有足够的注意力。进一步看,它应该归结为哪个构造函数是通过重载分辨率挑选出来的?Base b = Derived();' – chris 2015-04-01 01:37:55