MSVC和clang / gcc不同意是否可以在三元运算符中使用两个不同的积分常数(因此它们是否具有 common_type
):
#include <utility>
int main()
{
return false
? std::integral_constant<int, 1>()
: std::integral_constant<int, 2>();
}
上面的代码段在clang和gcc中编译得很好,但在MSVC中却没有。根据标准,正确的行为是什么?如果它是clang / gcc行为,那么用于推断这两种不同类型的常见类型的转换序列是什么?
tldr;代码格式正确。条件表达式将具有类型 int
和价值 2
。这是一个MSVC错误。
从[expr.cond]:
否则,如果第二个和第三个操作数具有不同的类型并且具有(可能是cv限定的)类类型或[...],则尝试从每个操作数形成隐式转换序列(13.3.3.1)到另一种的类型。 [...]尝试从操作数表达式形成隐式转换序列 E1
类型 T1
到与类型相关的目标类型 T2
操作数表达式 E2
如下: [...]
- 如果E2是左值,[...]
- 如果E2是x值,[...]
- 如果E2是prvalue,或者如果上面的转换序列都不能形成,并且至少有一个
operands具有(可能是cv-qualified)类类型:
- 如果T1和T2是相同的类类型(忽略cv-qualification),或者一个是另一个的基类,
T2至少与cv一样合格,目标类型为T2,
- 否则,目标类型是应用左值到右值(4.1)后E2所具有的类型,
数组到指针(4.2)和函数到指针(4.3)标准转换。
所以我们试图从类型中形成隐式转换序列 std::integral_constant<int, 1>
输入 std::integral_constant<int, 2>
。这不可行。反向的隐式转换序列也不可行。这些类型根本不可互换。
所以我们继续:
如果没有转换
可以形成序列,保持操作数不变,并如所描述的那样执行进一步的检查
下面。 [...]
如果第二个和第三个操作数是相同值类别的glvalues并且具有相同的类型,[...]
否则,结果是prvalue。如果第二个和第三个操作数不具有相同的类型,则任何一个
具有(可能是cv-qualified)类类型,重载决策用于确定转换(如果有)
适用于操作数(13.3.1.2,13.6)。如果重载解析失败,则程序格式错误。
好的,我们可以执行什么重载分辨率?来自[over.match.oper]:
如果任一操作数具有类或枚举类型,则可以声明用户定义的操作符函数来实现此操作符或 用户定义的转换可能是必要的 将操作数转换为
适合内置运算符的类型。
内核在[over.built]中指定为:
对于每对提升的算术类型L和R,存在该形式的候选运算符函数
LR operator?:(bool, L , R );
其中LR是类型L和R之间通常的算术转换的结果。
其中一个内置的将是 int operator?:(bool, int, int)
。以来 std::integral_constant<int, V>
确实有 operator int()
,这是两个论点的可行转换。
我们继续[expr.cond]:
否则,应用如此确定的转换,并使用转换的操作数代替本节其余部分的原始操作数。
执行Lvalue-to-rvalue(4.1),数组到指针(4.2)和函数到指针(4.3)标准转换
在第二和第三个操作数上。完成转换后,以下其中一项应成立:
- 第二和第三个操作数具有相同的类型;结果是该类型,并使用所选操作数初始化结果对象。
此时,第二和第三个操作数 做 具有相同的类型: int
。所以结果对象初始化为 int
,表达形式良好。
[expr.cond]的相关段落是 6:
否则,结果是prvalue。如果是第二和第三个操作数
不具有相同的类型,并且具有(可能是cv-qualified)
类类型,重载决策用于确定转换
(如果有的话)应用于操作数(13.3.1.2,13.6)。如果
重载决议失败,程序格式不正确。否则,
应用如此确定的转换和转换后的操作数
用于代替其余部分的原始操作数
部分。
integral_constant<int>
有一个转换运算符 int
,所以这可以工作。
接下来 13.3.1.2,我们看到了 第3.2段,所有内置的 ?:
采用整数和浮点参数的运算符是候选者。
现在,根据我们的三个参数,对所有这些执行重载解析。按照 [over.ics.rank] /3.3,我们通过比较来自的标准转换序列来打破 int
(返回类型 integral_constant<int>
转换运算符)到内置运算符的参数类型。
不过,一看 表13 足够;转换到浮点类型具有转换排名,从那以后 int
是一种提升类型,转换为任何整数类型 但 int
(这是身份转换)是转换排名的积分转换。因此,最明确的可行候选人是明确的, operator?:(bool, int, int)
。也就是说,MSVC是错误的。