考虑这段代码:
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 *'缺少演员Base * obj =!bar? foo:bar;
这意味着它要编译我必须将代码更改为:
Base* obj = !bar ? static_cast<Base*>(foo) : bar;
由于存在对Base*
的隐式强制转换,是什么阻止了编译器这样做?
换句话说,为什么Base* obj = foo;
在没有强制转换的情况下工作但使用?:
运算符却没有?是因为我不清楚我是否想要使用Base
部分?
答案 0 :(得分:21)
引用C ++标准草案N4296,第5.16节条件运算符,第6.3段:
- 第二个和第三个操作数中的一个或两个具有指针类型; 指针转换(4.10)和 资格转换(4.4)执行以将它们带到复合指针类型(第5条)。 结果是复合指针类型。
第5节表达式,第13.8和13.9段:
两个操作数p1和p2的复合指针类型,分别具有类型T1和T2,其中at 至少一个是指向成员类型或std :: nullptr_t的指针或指针,是:
- 如果T1和T2是类似的类型(4.4),则是T1和T2的cv组合类型;
- 否则,需要确定复合指针类型的程序格式不正确。
注意:我在这里复制了5 / 13.8只是为了告诉你它没有被击中。实际上有效的是5 / 13.9,&#34;该程序格式错误&#34;。
第4.10节指针转换,第3段:
类型为“指向cv D的指针”的prvalue,其中D是类类型,可以转换为类型为“指针”的prvalue 到cv B“,其中B是D的基类(第10条)。如果B是不可访问的(第11条)或含糊不清的(10.2) D的基类,需要这种转换的程序是不正确的。转换的结果是a 指向派生类对象的基类子对象的指针。空指针值转换为 目标类型的空指针值。
因此,Foo和Bar都来自同一个基类并不重要(根本不重要)。唯一重要的是指向Foo的指针和指向Bar的指针不能相互转换(没有继承关系)。
答案 1 :(得分:19)
允许转换为条件运算符的基本指针类型听起来不错,但在实践中会有问题。
在你的例子中
struct Base {};
struct Foo : Base {};
struct Bar : Base {};
对cond ? foo : bar
类型Base*
来说,这似乎是明显的选择。
但这种逻辑并不能解决一般情况
E.g:
struct TheRealBase {};
struct Base : TheRealBase {};
struct Foo : Base {};
struct 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
哎哟!!对cond ? foo : bar
类型的好运推理。我知道,丑陋丑陋,不实用,被追捕有价值,但标准仍然需要有规则。
你明白了。
并且还要记住,std::common_type
是根据条件运算符规则定义的。
答案 2 :(得分:5)
换句话说,为什么
Base* obj = foo;
在没有强制转换的情况下工作但是使用?:
运算符并不能解决问题?
条件表达式的类型不依赖于它所分配的内容。在您的情况下,编译器需要能够评估!bar ? foo : bar;
,无论它被分配给什么。
在您的情况下,这是一个问题,因为转换为foo
类型和bar
的{{1}}都不能转换为bar
类型。
是不是因为我不清楚我想使用Base部分?
正。
答案 3 :(得分:3)
由于存在对
Base*
的隐式强制转换,是什么阻止了编译器这样做?
根据[expr.cond] / 7,
在第二个和第三个操作数上执行左值到右值,数组到指针和函数到指针的标准转换。在这些转换之后,以下之一应该成立:
- ...
- 第二个和第三个操作数中的一个或两个具有指针类型;执行指针转换,函数指针转换和限定转换以将它们带到它们的复合指针类型。结果是复合指针类型。
其中复合指针类型在[expr.type] / 4中定义:
分别具有类型T1和T2的两个操作数p1和p2的复合指针类型,其中至少一个是指针或指向成员的类型或std :: nullptr_t,是:
如果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相关的参考相关,C1和cv组合类型分别为T2或cv组合型T2和T1;
如果T1是“指向cv1类型的C1的成员的指针”,T2是“指向cv2 U2类型的C2的成员的指针”,其中C1是与C2或C2相关的参考,则与C1有关的参考,cv-组合型T2和T1或cv-组合型T1和T2;
如果T1和T2是相似的类型,则为T1和T2的cv组合类型;
否则,需要确定复合指针类型的程序格式不正确。
现在你可以看到指向“common base”的指针不是复合指针类型。
换句话说,为什么
Base* obj = foo;
在没有强制转换的情况下工作但使用?:
运算符却没有?是因为我不清楚我是否想要使用Base
部分?
问题是规则应该独立地检测条件表达式的类型,而不需要观察初始化。
具体而言,规则应检测以下两个语句中的条件表达式是否为同一类型。
Base* obj = !bar ? foo : bar;
bar ? foo : bar;
现在,如果您毫不怀疑第二个语句中的条件表达式是格式错误的 1 ,那么在第一个语句中使其格式正确的原因是什么?
1 当然,人们可以制定一个规则来使这种表达形式良好。例如,让复合指针类型包含指向明确基类型的指针。但是,这超出了这个问题,应该由ISO C ++委员会讨论。