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::nullptrt
- 如果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;