C++ 三元运算符隐式转换为基类

C++ 三元运算符隐式转换为基类,c++,language-lawyer,c++17,ternary-operator,implicit-conversion,C++,Language Lawyer,C++17,Ternary Operator,Implicit Conversion,考虑这段代码: struct Base { int x; }; struct Bar : Base { int y; }; struct Foo : Base { int z; }; Bar* bar = new Bar; Foo* foo = new Foo; Base* returnBase() { Base* obj = !bar ? foo : bar; return obj; } int main() { returnBase(

考虑这段代码:

struct Base
{
    int x;
};

struct Bar : Base
{
    int y;
};

struct Foo : Base
{
    int z;
};

Bar* bar = new Bar;
Foo* foo = new Foo;

Base* returnBase()
{
    Base* obj = !bar ? foo : bar;
    return obj;
}

int main() {
    returnBase();
    return 0;
}
这在Clang或GCC下不起作用,给我:

错误:不同指针类型“Foo*”之间的条件表达式 而“Bar*”缺少一个演员基础*obj=!酒吧?福:酒吧

这意味着要编译它,我必须将代码更改为:

Base* obj = !bar ? static_cast<Base*>(foo) : bar;
Base*obj=!酒吧?静态_-cast(foo):条形;
既然存在对
Base*
的隐式强制转换,那么是什么阻止编译器这样做的呢

换句话说,为什么
Base*obj=foo不使用强制转换但使用
?:
运算符不工作?是否因为不清楚我是否要使用
Base
部分

换句话说,为什么
Base*obj=foo不使用强制转换但使用
?:
运算符不工作

条件表达式的类型不取决于它被分配到的对象。在您的情况下,编译器需要能够计算
!酒吧?福:酒吧无论分配给什么

在您的情况下,这是一个问题,因为转换为
bar
类型的
foo
bar
都不能转换为
foo
类型

是不是因为不清楚我是否要使用基础部件


>

< P>引用C++标准草案N429,第5.16节条件运算符,第6.3段:

  • 第二个和第三个操作数中的一个或两个都具有指针类型指针转换(4.10)和 执行限定转换(4.4),将其转换为复合指针类型(第5条)。 结果是复合指针类型
第5节表达式,第13.8和13.9段:

分别具有T1和T2类型的两个操作数p1和p2的复合指针类型,其中 至少一个是指针或指向成员类型或std::nullptr_t的指针,即:

  • 如果T1和T2为相似类型(4.4),则cv组合类型为T1和T2
  • 否则,需要确定复合指针类型的程序格式不正确
注意:我在这里复制了5/13.8只是为了告诉你它没有命中。实际上是5/13.9,“程序格式不正确”

和第4.10节指针转换,第3段:

“指向cv D的指针”类型的PR值(其中D是类类型)可以转换为“指针”类型的PR值 对于cv B”,其中B是D的基类(第10条)。如果B是不可访问的(第11条)或不明确的(第10.2条) D的基类,需要进行此转换的程序是格式不正确的。转换的结果是 指向派生类对象的基类子对象的指针。空指针值将转换为 目标类型的空指针值


所以,Foo和Bar都是从同一个基类派生的并不重要。只有指向Foo的指针和指向Bar的指针不能相互转换(没有继承关系)才重要。

允许将条件运算符转换为基指针类型听起来不错,但在实践中会有问题

在你的例子中

struct Base {};
struct Foo : Base {};
struct Bar : Base {};
这似乎是
cond类型的明显选择?foo:bar
将成为
Base*

但这种逻辑并不适用于一般情况

例如:

应该
cond吗?foo:bar
应为
Base*
型还是
TheRealBase*

那么:

struct Base1 {};
struct Base2 {};
struct Foo : Base1, Base2 {};
struct Bar : Base1, Base2 {};
什么类型的
cond?foo:bar
现在可以吗

或者现在呢:

struct Base {};

struct B1 : Base {};
struct B2 : Base {};

struct X {};

struct Foo : B1, X {};
struct Bar : B2, X {};


      Base
      /  \
     /    \   
    /      \
  B1        B2 
   |   X    |
   | /   \  |
   |/     \ |
  Foo      Bar
哎哟!!一种<代码>条件的好运推理?foo:bar
。我知道,丑陋、不实用、值得猎杀,但标准对此仍有规定

你明白了

还要记住,
std::common_type
是根据条件运算符规则定义的

既然存在对
Base*
的隐式强制转换,那么是什么阻止编译器这样做的呢

根据[expr.cond]/7

对第二个和第三个操作数执行左值到右值、数组到指针和函数到指针的标准转换。在这些转换之后,以下其中一项应保持不变:

  • 第二个和第三个操作数中的一个或两个都具有指针类型;执行指针转换、函数指针转换和限定转换,将它们转换为复合指针类型。结果是复合指针类型
其中,复合指针类型在[expr.type]/4中定义:

两个操作数p1和p2的复合指针类型,分别具有T1和T2类型,其中至少有一个是指针或指向成员类型或std的指针​::​nullptr\ut是:

  • 如果p1和p2都是空指针常数,则std​::​nullptr­t

  • 如果p1或p2是空指针常数,则分别为T2或T1

  • 如果T1或T2是“指向cv1 void的指针”,而另一种类型是“指向cv2 T的指针”,其中T是对象类型或void,“指向cv12 void的指针”,其中cv12是cv1和cv2的并集

  • 如果T1或T2是“指向noexcept函数的指针”,而另一种类型是“指向函数的指针”,其中函数类型在其他方面相同,“指向函数的指针”

  • 如果T1为“指向cv1 C1的指针”,T2为“指向cv2 C2的指针”,其中C1为与C2相关的参考,或C2为与C1相关的参考,则分别为T1和T2的cv组合类型或T2和T1的cv组合类型

  • 如果T1是“指向cv1 U1类型C1的成员的指针”,T2是“指向cv2 U2类型C2的成员的指针”,其中C1是与C2相关的参考,或者C2是与C1相关的参考,则分别为T2和T1的cv组合类型或T1和T2的cv组合类型

    struct Base {}; struct B1 : Base {}; struct B2 : Base {}; struct X {}; struct Foo : B1, X {}; struct Bar : B2, X {}; Base / \ / \ / \ B1 B2 | X | | / \ | |/ \ | Foo Bar
Base* obj = !bar ? foo : bar;
bar ? foo : bar;