2017-02-23 73 views
11

MSVC和铛/ GCC不同意是否两个不同的积分常数可以在三元操作员使用(并因此它们是否有common_type):integral_constants在三元运算

#include <utility> 

int main() 
{ 
    return false 
     ? std::integral_constant<int, 1>() 
     : std::integral_constant<int, 2>(); 
} 

上面代码段编译细在clang和gcc中,但不在MSVC中。根据标准,正确的行为是什么?如果它是clang/gcc行为,那么用于推导这两种不同类型的通用类型的转换序列是什么?

+0

那么,它们都可以隐式转换为'int',我认为这是它们的通用类型。 – user2079303

+0

他们是,但我试图决定是否允许编译器寻找匹配。这是不明确的部分。 –

+0

[相关问题](http://stackoverflow.com/questions/29381171/ternary-operator-of-different-types)? –

回答

8

tldr;代码格式良好。条件表达式将具有类型int和值2。这是一个MSVC错误。


从[expr.cond]:

否则,如果所述第二和第三操作数具有不同的类型和或者具有(可能CV修饰)类型,或[...],试图从这些操作数中的每一个到另一个操作数的类型形成隐式转换序列(13.3.3.1)。 [...]尝试形成从T1类型的操作数表达式E1到与T2T2类型相关的目标类型的隐式转换序列,如下所示:
- 如果E2是
- 如果E2是一个xvalue,
- 如果E2是一个prvalue,或者如果上面的转换序列都不能形成,并且至少有一个操作数具有(可能CV修饰)类类型:
            - 如果T1和T2是相同的类类型(IG或非(NOR)CV-资格),或一个是基类的其他, 和T2是至少为CV-限定为T1时,目标类型为T2,
            - 否则,目标类型为在应用左值到右值(4.1), 数组到指针(4.2)和函数到指针(4.3)标准转换之后,E2将具有的类型。

因此,我们试图形成一个从std::integral_constant<int, 1>类型到std::integral_constant<int, 2>类型的隐式转换序列。这不可行。隐式转换序列在相反方向也不可行。这些类型根本不可互换。

因此,我们继续:

如果不能形成转换 序列,操作数保持不变并如下所述 进行进一步检查。 [...]

如果第二和第三运算数是相同的值的类别和glvalues具有相同的类型,[...]

否则,结果是一个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,表达式格式良好。

6

从[expr.cond]有关段落是6

否则,结果是一个prvalue。如果第二个和第三个操作数 不具有相同的类型,并且具有(可能为cv限定的)类类型,则使用重载决策来确定要应用于操作数的转换 (如果有的话)(13.3.1.2 ,13.6)。如果重载解析失败,则该程序格式不正确。否则,应用这样确定的转换结果,并使用转换后的操作数 代替此 部分其余部分的原始操作数。

integral_constant<int>有一个转换运算符到int,所以这可以工作。 继续到13.3.1.2,我们看到,通过paragraph 3.2,所有内置的?:运算符都取整数和浮点参数。

现在重载决议是为所有这些,给我们三个参数执行。根据[over.ics.rank]/3.3,我们通过比较intintegral_constant<int>的转换运算符的返回类型)与内置运算符的参数类型的标准转换序列来进行tie-break。

但是,看看table 13就足够了;转换为浮点类型具有转换等级,并且由于int是升级类型,转换为任何整数类型int(它是标识转换)是具有转换等级的整数转换。因此,最好的可行的候选人,明确地说,operator?:(bool, int, int)。也就是说,MSVC是错误的。