具有相同基础类类型的条件运算符

Conditional operator with same underlying class type

本文关键字:类型 条件运算符 基础类      更新时间:2023-10-16

这个程序应该输出 0 还是 1?在我对 C++14 标准中引用段落的阅读和理解中,它应该打印 1,但 GCC 和 clang 都打印 0(因为推断的类型是A const而不是A const&):

#include <iostream>
struct A {};
int main()
{
A a;
A const& ra = std::move(a); // #1
std::cout << std::is_same<decltype(true ? ra : std::move(a)),
A const&>::value; // Prints 0
}

在本例中,raA const的左值,std::move(a)Ax值,两者都是类类型。根据关于条件运算符的标准(强调我的),结果应该是类型A constlvalue,因此decltype结果必须A const&

[expr.cond]/3否则,如果第二个和第三个操作数具有不同的类型,并且具有(可能符合 cv 条件的)类 类型,或者如果两者都是相同值类别的 GL 值并且除 CV 资格外属于同一类型,则 尝试将每个操作数转换为另一个操作数的类型。确定过程 是否可以转换类型 T1 的操作数表达式 E1 以匹配类型的操作数表达式 E2 T2 定义如下:

— 如果E2 是左值:如果 E1 可以隐式转换为 E2(第 4 条),则可以将 E1 转换为匹配 键入"对 T2 的左值引用">,但受以下约束:在转换中,引用必须直接绑定 (8.5.3) 到左值

[...]

在这种情况下,E2ra,这是一个左值,另一个可以隐式转换为"对 T2 的左值引用">,如第// #1行所示。"对 T2 的左值引用">被翻译为A const&,因此,std::move(a)直接绑定到类型A const的左值,转换后,两个操作数具有相同的类型和值类别,因此:

[expr.cond]/3如果第二个和第三个操作数是相同值类别的 glvalue 并且具有相同的类型,则结果属于该类型和值类别 [...]。

因此,运算符结果应该是左值,decltype结果应该是引用,因此程序应该打印 1。

这个问题措辞很尴尬。相反,您应该询问表达式true ? ra : std::move(a)的类型和值类别应该是什么。这个问题的答案是A const型的 prvalue .这随后意味着程序应该打印 0,因为我认为每个编译器都正确。


?:的规则相当复杂。在这种情况下,我们有两个类类型的表达式,我们尝试根据有限的规则子集查看是否可以相互转换。

尝试转换rastd::move(a)失败。我们首先尝试使用不能直接绑定到ra的目标类型是A&&。然后,我们在 (3.3.1) 中尝试备份计划,因为这两个表达式具有相同的底层类类型,但我们的目标表达式至少不像源表达式那样符合 cv 资格,因此这也失败了。

尝试转换std::move(a)ra失败(3.1),因为我们需要直接绑定到左值(我们可以将右值绑定到常量左值引用,但这里我们需要绑定左值)。但是,(3.3.1) 备份成功,因为现在目标类型至少与源一样符合 cv 条件。

因此,我们应用转换并继续,就好像第二个操作数是A const类型的左值,但第三个操作数现在是A const类型的 prvalue(而不是类型A的 xvalue)。

(4) 失败,因为它们不属于同一值类别。

因此,结果是一个价值。由于它们具有相同的类型,因此结果是该类型:A const.