不同类型的三元运算符 [英] ternary operator of different types

查看:90
本文介绍了不同类型的三元运算符的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码在g ++ 4.9.2和clang ++ 3.7.0下的行为不同。哪一个是对的?标准中的什么部分与此相关?感谢。

The following piece of code behaves differently under g++ 4.9.2 and clang++ 3.7.0. Which one is correct? What part in standard is related to this? Thanks.

#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 ++给出一个错误类型('Derived'和'Base')

g++ accepts it and clang++ gives an error incompatible operand types ('Derived' and 'Base'). See below for details.

[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方便, 5.12 [expr.cond] / 3包含这个(强调我的):

I don't have N3936 handy, but N3797 §5.12 [expr.cond]/3 contains this (emphasis mine):


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

Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. The process for determining whether an operand expression E1 of type T1 can be converted to match an operand expression E2 of type T2 is defined as follows:


  • 如果E2是小值:[removed]

  • 如果E2是xvalue:[removed]

  • E2是一个prvalue,或者如果两个转换
    都不能做,并且至少有一个操作数有(可能
    cv限定)类类型:

    • 如果 E1和E2具有类类型,并且
      基础类类型相同或一个是
      其他
      的基类, :

      如果T2的类别与或基类相同,
      类型为T1,类别为T1,则E1可以转换为匹配E2 T2的cv-qualification
      与T1的cv-qualification是相同的cv-qualification为cv-qualification
      。如果应用了转换,则通过从E1中复制初始化临时的
      类型T2并将该临时值用作转换后的操作数,将E1转换为类型T2的prval值。

    • If E2 is an lvalue: [removed]
    • If E2 is an xvalue: [removed]
    • If E2 is a prvalue or if neither of the conversions above can be done and at least one of the operands has (possibly cv-qualified) class type:
      • if E1 and E2 have class type, and the underlying class types are the same or one is a base class of the other:
        E1 can be converted to match E2 if the class of T2 is the same type as, or a base class of, the class of T1, and the cv-qualification of T2 is the same cv-qualification as, or a greater cv-qualification than, the cv-qualification of T1. If the conversion is applied, E1 is changed to a prvalue of type T2 by copy-initializing a temporary of type T2 from E1 and using that temporary as the converted operand.

      使用此过程,确定 be
      转换为匹配第三个操作数
      ,以及第三个操作数
      是否可以转换为匹配第二个操作数。如果两者都可以
      转换,或者一个可以转换,但转换不明确,
      程序是不成形的。如果两者都不能转换,则操作数
      保持不变,并且如下所述进行进一步检查
      如果只能进行一次转换,则将转换
      应用于所选的操作数,并将转换的操作数用于本节其余部分的原始操作数的
      中。
      / p>

      Using this process, it is determined whether the second operand can be converted to match the third operand, and whether the third operand can be converted to match the second operand. If both can be converted, or one can be converted but the conversion is ambiguous, the program is ill-formed. If neither can be converted, the operands are left unchanged and further checking is performed as described below. If exactly one conversion is possible, that conversion is applied to the chosen operand and the converted operand is used in place of the original operand for the remainder of this section.

      现在复制初始化中的最后 Base > Derived(),我们可以看看§13.3.1.3[over.match.ctor]:

      Now to copy-initialize the final Base operand from Derived(), we can look at §13.3.1.3 [over.match.ctor]:


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

      When objects of class type are direct-initialized (8.5), or copy-initialized from an expression of the same or a derived class type (8.5), overload resolution selects the constructor. For direct-initialization, the candidate functions are all the constructors of the class of the object being initialized. For copy-initialization, the candidate functions are all the converting constructors (12.3.1) of that class. The argument list is the expression-list or assignment-expression of the initializer.

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

      Converting constructors are defined as follows in §12.3.1 [class.conv.ctor]:


      没有function-specifier明确声明的构造函数
      指定转换从其参数的类型到
      类的类。这样的构造函数称为转换构造函数。

      A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters to the type of its class. Such a constructor is called a converting constructor.

      现在,如果你相信我(为了不必引用更多比起我有13.3),一个prvalue Derived()将导致重载解决方案选择move构造函数(采取 Base&& ),虽然已删除,但此会导致Clang的错误。

      Now, if you'll believe me (for the sake of not having to quote more than I have of 13.3) that a prvalue Derived() will cause overload resolution to choose the move constructor (taking Base&&), despite being deleted, this causes the error from Clang.

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

      In conclusion, Clang is correct in issuing an error. As using a deleted function requires a diagnostic, this is a bug in GCC.

      这篇关于不同类型的三元运算符的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆