integral_constants三元运算符
integral_constants in ternary operator
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 错误。
From [expr.cond]:
否则,如果第二个和第三个操作数具有不同的类型,并且具有(可能符合 cv 限定的)类类型,或者 [...],则尝试形成从每个操作数到另一个操作数类型的隐式转换序列 (13.3.3.1)。[...]尝试形成从类型
T1
的操作数表达式E1
到与操作数E2
表达式的类型T2
相关的目标类型的隐式转换序列,如下所示:[...]
— 如果 E2 是左值, [...]
— 如果 E2 是一个 x值, [...]
— 如果 E2 是一个 prvalue,或者如果上述转换序列都不能形成,并且至少有一个 操作数具有(可能符合 cv 条件)类类型:
— 如果 T1 和 T2 是相同的类类型(忽略 cv 资格),或者一个是另一个的基类, 并且 T2 至少与 T1 一样符合 cv 条件,目标类型为 T2,
否则,目标类型是 E2 在应用左值到右值 (4.1) 后将具有的类型, 数组到指针 (4.2) 和函数到指针 (4.3) 标准转换。
因此,我们尝试形成从类型std::integral_constant<int, 1>
到类型std::integral_constant<int, 2>
的隐式转换序列。这是不可行的。反向的隐式转换序列也不可行。这些类型根本无法相互转换。
所以我们继续:
如果没有转换 可以形成序列,操作数保持不变,并按照描述执行进一步检查 下面。[...]
如果第二个和第三个操作数是相同值类别的 glvalue 并且具有相同的类型,则 [...]
否则,结果是一个 prvalue。如果第二个和第三个操作数的类型不同,并且 具有(可能符合 CV 条件的)类类型,重载分辨率用于确定要 应用于操作数 (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]:
否则,将应用这样确定的转换,并在本节的其余部分使用转换后的操作数代替原始操作数。
执行左值到重值 (4.1)、数组到指针 (4.2) 和函数到指针 (4.3) 标准转换 在第二个和第三个操作数上。完成这些转换后,应保留以下条件之一:
— 第二个和第三个操作数具有相同的类型;结果属于该类型,并且使用选定的操作数初始化结果对象。
此时,第二个和第三个操作数确实具有相同的类型:int
。因此,结果对象被初始化为int
,并且表达式的格式正确。
[expr.cond] 中的相关段落是 6:
否则,结果是一个 prvalue。如果第二个和第三个操作数 没有相同的类型,并且具有(可能符合 CV 条件) 类类型,重载分辨率用于确定转换 (如果有的话)应用于操作数(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 是错误的。
- 三元运算符和 if constexpr
- 在 c++ 中三元运算符中不允许继续(关键字)吗?
- 三元运算符在返回语句中给出意外的结果
- 递归中三元运算符的奇怪行为
- 错误:三元运算符无法在对象中正常工作"cout"
- 是否允许三元运算符在C++中计算两个操作数?
- 有条件地选择带有 decltype() 和三元运算符的类型
- 三元运算符在C++中的意外行为
- 有没有办法将 for 循环结果返回到像三元运算符这样的函数中?
- 变量值,在三元运算符之后
- 是否可以在C++中使用三元运算符在 if 语句中选择比较运算符?
- c++中的增量和三元运算符优先级
- C++中三元运算符的意外行为
- 用于返回开关的三元运算符
- 替换模板元编程中的三元运算符
- 三元运算符 '?:' 在 4.9.0 之前的 GCC 版本中推断出不正确的类型?
- 在 std::map 上使用三元运算符和 std::更大的附加参数
- "Do nothing"在三元运算符的其他部分?
- 如何确定三元运算符的返回类型?
- 无法使用三元运算符有条件地分配"istream &"?