在回答关于枚举重载解析的问题时出现了这个问题。
虽然long long
的情况绝对是MSVC2012NovCTP中的错误(根据标准文本和gcc 4.7.1的测试),但我无法找出为什么会发生以下行为:
#include <iostream>
enum charEnum : char { A = 'A' };
void fct(char) { std::cout << "fct(char)" << std::endl; }
void fct(int) { std::cout << "fct(int)" << std::endl; }
void fct(long long) { std::cout << "fct(long long)" << std::endl; }
int main()
{
fct('A');
fct(A);
}
MSVC2012NovCTP和gcc 4.7.1都同意这个输出:
fct (char)
fct (int)
A
不应该由charEnum
转化为char
吗?为什么A
被转换为int
?
EDIT: clang抱怨调用是模糊的,这与我下面的解释一致;也就是说,如果只把它看作底层类型,我还是会觉得更直观。
两个相关的标准摘录是§7.2/9:
通过整型提升(4.5)将枚举数或无作用域枚举类型的对象的值转换为整数
和§4.5/4:
基础类型为固定(7.2)的非作用域枚举类型的右值可以转换为其基础类型的右值。此外,如果可以将整型提升应用于其基础类型,则基础类型固定的无作用域枚举类型的右值也可以转换为提升的基础类型的右值。
因此,charEnum
既可以转换为char
,也可以对char
进行积分提升,如int
。
但这对我来说是模糊的,因为"can"并没有完全说明实际会选择哪个。如果有的话,这种措辞应该是模棱两可的,因为在char
或其任何促销活动之间没有给出偏好。如果注释掉fct(int)
,则调用是不明确的。int
为什么特别?
我唯一能想到的是积分提升是递归地应用的,但我没有看到任何强制要求。
在c++ 03,规则是:
可以是无作用域枚举类型(7.2 [dcl.enum])的右值转换为下列类型中的第一个右值,该类型可以表示枚举的所有值(即如7.2 [dcl.enum]中所述,范围bmin到bmax: int, unsigned int;Long int、unsigned Long int、Long Long int或unsigned Long Long int
在c++ 03编译器中,int
将被选择,因为它是第一个在名单上。
在c++ 11中,引入了底层类型。因此,通过685。枚举的整体提升忽略了固定的基础类型,该措辞被更改为您在§4.5/4中引用的段落,并且从阅读缺陷报告来看,委员会的意图似乎是选择
fct(char)
(基础类型)。然而,根据核心问题1601下的讨论,c++ 11中的文本实际上使转换变得模棱两可(fct(char)
和fct(int)
都是可能的,但都不是首选)。
c++ 14中提出并接受了以下修复:
提升底层类型为的枚举的转换固定到其基础类型比提升到如果两者不同,则提升底层类型。
由于它被报告为c++ 11中的缺陷,编译器应该在c++ 11模式下应用此修复并调用fct(char)
.
根据我对当前标准的解释,调用必须是不明确的。
Per 4.5/4:
"基础类型为固定(7.2)的非作用域枚举类型的右值可以转换为其基础类型的右值。此外,如果可以将整型提升应用于其基础类型,则基础类型固定的无作用域枚举类型的右值也可以转换为提升的基础类型的右值。"
这提供了两个可选的提升:提升到基础类型,以及提升到提升的基础类型。因此,在解析对重载函数的函数调用时,应该使用中的哪一个,仅这一段就引入了歧义。
然后,第13.3.3段根据"转换序列"确定哪个是重载集的最佳可行函数。具体来说,与此相关的是13.3.3.1("隐式转换序列")和13.3.3.1.1("标准转换序列"),它们定义了这些转换序列由哪些基本步骤组成。
13.3.3.1.1/1和表12将这些步骤分为四类,其中Promotion和Conversion,并根据构成这些序列的单个转换的类别对转换序列进行排序。
在本例中,有两个由单个提升步骤组成的单长度转换序列(都是4.5./4所允许的)。转换序列按照13.3.3.2对进行排序。特别是,13.3.3.2/3提到,如果:
,转换序列S1优于转换序列S2:" S1的秩[即升序、转换等]优于 S2的秩,或者S1和S2具有相同的秩,并且可以通过下段中的规则区分,或者,如果不是,则[…]"
下面提到的"段落"是13.3.3.2/4,它说:
"标准转换序列按其等级排序:精确匹配是比提升更好的转换,提升是比转换更好的转换。具有相同秩的两个转换序列是不可区分的,除非下列规则之一适用:"
那么,接下来的是一组规则,不适用于。因此,我们有两个单步转换序列,由一个相同秩的提升组成。根据上述对当前标准的解释,调用必须是二义性的。但是,我个人认为,需要进行更改,使转换为无作用域枚举的固定底层类型比其他可能的转换更可取。