不同类型的三元运算符

Kan*_* Li 17 c++ c++11 c++14

下面的代码在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();
}
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)

chr*_*ris 7

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

否则,如果第二个和第三个操作数具有不同的类型并且具有(可能是cv限定的)类类型,或者两者都是相同值类别的glvalues和除cv-qualification之外的相同类型,则尝试转换每个那些操作数与另一种操作数的关系.确定类型T1的操作数表达式E1是否可以转换为匹配类型T2的操作数表达式E2的过程定义如下:

  • 如果E2是左值:[已删除]
  • 如果E2是x值:[已删除]
  • 如果E2是prvalue,或者上面的转换都不能完成,并且至少有一个操作数具有(可能是cv-qualified)类类型:
    • 如果E1和E2具有类类型,并且底层类类型相同或者一个是另一个类的基类:
      如果T2的类与类的类型相同,则可以转换为E1,或者基类类, T1的类别和T2的cv资格是与cv资格相同的cv资格或更高的cv资格.如果应用转换,则通过从E1 复制初始化 T2类型的临时值并将该临时值用作转换后的操作数,将E1更改为类型T2的prvalue .

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

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

当类类型的对象被直接初始化(8.5),或 从相同或派生类类型(8.5)的表达式进行复制初始化时,重载决策选择构造函数.对于直接初始化,候选函数是正在初始化的对象的类的所有构造函数.对于复制初始化,候选函数是该类的所有转换构造函数(12.3.1).参数列表是初始化程序的表达式列表或赋值表达式.

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

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

现在,如果你相信我(为了不必引用比13.3更多的引用),prvalue Derived()将导致重载决策选择移动构造函数(take Base&&),尽管被删除,这会导致Clang的错误.

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

  • 文本似乎是说'Derived()`是**切片**来创建一个新的`Base()`,然后在选择该项时绑定到引用.这是一种令人惊讶的行为. (2认同)