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

Som*_*ken 48 c++ ternary-operator language-lawyer implicit-conversion c++17

考虑一下这段代码:

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

这在Clang或GCC下不起作用,给我:

错误:不同指针类型'Foo*'和'Bar*'之间的条件表达式缺少一个强制转换Base*obj =!bar?foo:bar;

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

Base* obj = !bar ? static_cast<Base*>(foo) : bar;
Run Code Online (Sandbox Code Playgroud)

由于隐式转换为Base*存在,是什么阻止编译器这样做?

换句话说,为什么没有Base* obj = foo;演员而是使用?:操作员的工作呢?是因为我不清楚我想使用这Base部分吗?

iBu*_*Bug 21

引自C++标准草案N4296,第5.16节条件运算符,第6.3段:

  • 第二和第三操作数中的一个或两个具有指针类型; 指针转换(4.10)和资格的转化(4.4)被执行以使它们以它们的复合指针类型(第5章).结果是复合指针类型.

第5节表达,第13.8和13.9段:

的两个操作数p1和具有类型T1和T2 P2的复合指针类型,分别为,其中至少一个是一个指针或指针构件类型或std :: nullptr_t,是:

  • 如果T1和T2是类似的类型(4.4),则cv组合类型的T1和T2;
  • 否则,需要确定复合指针类型的程序是不正确的.

注意:我在这里复制了5/13.8只是为了告诉你它没有命中.实际上有效的是5/13.9,"该程序是不正确的".

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

类型为"指向cv D的指针"的prvalue,其中D是类类型,可以转换为类型为"指向cv B的指针"的prvalue,其中B是D的基类(子句10).如果B是D不可访问(第11条)或模糊(10.2)基类,需要这种转换的程序是不正确的.转换的结果是指向派生类对象的基类子对象的指针.空指针值将转换为目标类型的空指针值.

因此,Foo和Bar都来自同一个基类并不重要(根本没有).唯一重要的是指向Foo的指针和指向Bar的指针不能相互转换(没有继承关系).

  • 唯一的原因我使用指针,惹恼其他人.</ S> (3认同)
  • @StoryTeller参考更新了更多相关文本 (2认同)

bol*_*lov 19

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

在你的例子中

struct Base {};
struct Foo : Base {};
struct Bar : Base {};
Run Code Online (Sandbox Code Playgroud)

这似乎是对的类型显而易见的选择cond ? foo : barBase*.

但这种逻辑并不能解决一般情况

例如:

struct TheRealBase {};
struct Base : TheRealBase {};
struct Foo : Base {};
struct Bar : Base {};
Run Code Online (Sandbox Code Playgroud)

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

怎么样:

struct Base1 {};
struct Base2 {};
struct Foo : Base1, Base2 {};
struct Bar : Base1, Base2 {};
Run Code Online (Sandbox Code Playgroud)

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

哎哟!!好运推理一种类型的cond ? foo : bar.我知道,丑陋丑陋,不实用,被追捕有价值,但标准仍然需要有规则.

你明白了.

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

  • 嗯......由于含糊不清,这些是不可行的情况的优秀例子.但只要转换是明确的,C++就有许多转换规则_do work_.喜欢这个问题.没有? (16认同)

R S*_*ahu 5

换句话说,为什么没有Base* obj = foo;演员而是使用?:操作员的工作呢?

条件表达式的类型不依赖于它所分配的内容.在您的情况下,!bar ? foo : bar;无论分配给什么,编译器都需要能够进行评估.

在您的情况下,这是一个问题,因为既没有foo转换为类型barbar不能转换为类型foo.

是因为我不清楚我想使用Base部分吗?

正是.

  • *"表达式的类型不依赖于它所分配的内容."*一般情况下这不是真的.命名一组重载函数的表达式本身不能键入(例如,decltype失败),但在相应的上下文中获得正确的类型.也许将其调整为"有条件的类型......". (2认同)