C++ 不同类型的三元算子

C++ 不同类型的三元算子,c++,c++11,c++14,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

下面的代码在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();
}

我手头没有N3936,但N3797§5.12[expr.cond]/3包含以下内容(我的重点):

否则,如果第二个和第三个操作数具有不同的类型和 具有(可能是cv限定的)类类型,或者两者都是glvalues 具有相同的价值类别和相同的类型,但 cv限定,尝试转换每个操作数 与另一个的类型相同。用于确定是否存在以下情况的过程: T1类型的操作数表达式E1可以转换为与操作数匹配 T2型表达式E2定义如下:

  • 如果E2是左值:[已删除]
  • 如果E2是一个xvalue:[已删除]
  • 如果E2是prvalue,或者如果两种转换都不存在 可以执行上述操作,并且至少有一个操作数(可能是 cv(合格)等级类型:
    • 如果E1和E2具有类类型,则 基础类类型相同或一个是 其他
      如果T2的等级相同,则可以将E1转换为与E2匹配 输入为、或基本类、T1类和cv资格 T2的cv资格与相同,或更高的cv资格 比,T1的cv资格。如果应用了转换,则E1为 通过复制初始化更改为T2类型的PR值 从E1中键入T2,并使用该临时值作为转换的操作数
使用此过程,确定是否可以使用第二个操作数 转换为匹配第三个操作数,以及第三个操作数 可以转换为与第二个操作数匹配。如果两者都可以 转换,或者一个可以转换,但转换不明确, 这个程序格式不好。如果两者都不能转换,则操作数 保持不变,并按说明执行进一步检查 下面如果只能进行一次转换,则该转换为 应用于选定的操作数,并在 本节剩余部分的原始操作数的位置。

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

类类型的对象直接初始化时(8.5),或 从相同或派生类的表达式初始化副本 键入(8.5),重载解析选择构造函数。对于 直接初始化时,候选函数都是 正在初始化的对象的类的构造函数用于 复制初始化时,候选函数都是转换函数 该类构造函数(12.3.1)。参数列表是 初始值设定项的表达式列表或赋值表达式

转换构造函数的定义见§12.3.1[类别转换构造函数]:

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

现在,如果您相信我(为了不必引用比13.3更多的内容),prvalue
Derived()
将导致重载解析选择移动构造函数(采用
Base&
),这将导致Clang错误


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

Clang可能希望您使用显式cast@dyp:Chris没有说复制初始化意味着使用复制构造函数,这是错误的:如果有移动构造函数,则可能不需要复制构造函数。他说既然有一个复制构造函数,复制初始化应该成功,这是真的,要么有一个可用的移动构造函数,要么没有,然后复制构造函数就被使用了。@BenVoigt,没关系,当它说复制初始化时,我真的没有足够的注意。再进一步看,应该归结为
Base b=Derived()的重载解析选择了哪个构造函数@dyp,也许值得让叮当声的家伙们知道这种情况下的异常错误消息?我承认,这是相当做作的。切题的问题是:有没有办法做到OP想做的事情?(即,将引用直接绑定到
Base()
派生()
视情况而定,并延长临时对象的寿命?Clang确实在尝试移动它:我很困惑,为什么这里涉及移动构造函数?它试图移动什么,它试图移动到哪里?@dyp,那将是我的错。让我回去看看。@chris,从你的回答中仍然不清楚-也许这是你的an暗示的回答:但我更希望有更明确的东西-无论程序是否格式错误,gcc和clang中的哪一个是正确的。文本似乎在说,
Derived()
被切片,以生成一个新的
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.