我被MSVC无意识的C ++函数匹配所困扰。我可以将它减少到以下测试用例:
#include <iostream>
enum Code { aaa, bbb };
struct MyVal {
Code c;
MyVal(Code c): c(c) { }
};
void test(int i, MyVal val) {
std::cout << "case " << i << ": value " << val.c << std::endl;
}
void test(int i, double* f) {
std::cout << "case " << i << ": WRONG" << std::endl;
}
const Code v1 = aaa;
Code v2 = aaa;
const Code v3 = bbb;
int main() {
const Code w1 = aaa;
Code w2 = aaa;
const Code w3 = bbb;
test(1, v1); // unexpected MSVC WRONG
test(2, v2);
test(3, v3);
test(4, aaa);
test(5, w1); // unexpected MSVC WRONG
test(6, w2);
test(7, w3);
return 0;
}
我希望所有7次测试调用都匹配第一次重载,GCC(live example)和Clang(live example)按预期匹配:
case 1: value 0
case 2: value 0
case 3: value 1
case 4: value 0
case 5: value 0
case 6: value 0
case 7: value 1
但是MSVC(live example)将案例1和5与“错误”重载相匹配(我在MSVC 2013和2015中发现了这种行为):
case 1: WRONG
case 2: value 0
case 3: value 1
case 4: value 0
case 5: WRONG
case 6: value 0
case 7: value 1
对于具有(偶然)值0的const枚举变量,MSVC似乎首选转换为指针。我原本期望这种行为的文字为0,但不是枚举变量。
我的问题:MSVC行为符合标准吗? (也许对于旧版本的C ++?)如果没有,这是一个已知的扩展或错误吗?
答案 0 :(得分:5)
您没有为任何标准命名,但让我们看看有什么不同之处:
[C++11: 4.10/1]:
空指针常量是整数类型的整数常量表达式(5.19)prvalue,其求值为零或类型为std::nullptr_t
的prvalue。空指针常量可以转换为指针类型;结果是该类型的空指针值,并且可以与对象指针或函数指针类型的每个其他值区分开。这种转换称为空指针转换。相同类型的两个空指针值应相等。将空指针常量转换为指向cv限定类型的指针是单个转换,而不是指针转换后跟限定的序列。 [..]
[C++11: 5.19/3]:
文字常量表达式是文字类型的prvalue核心常量表达式,但不是指针类型。整数常量表达式是整数或未整数枚举类型的文字常量表达式。 [..]
和
[C++03: 4.10/1]:
空指针常量是整数类型的整数常量表达式(5.19)rvalue,其值为零。空指针常量可以转换为指针类型;结果是该类型的空指针值,并且可以与指向对象的指针或指向函数类型的指针的每个其他值区分开来。相同类型的两个空指针值应相等。将空指针常量转换为指向cv限定类型的指针是单个转换,而不是指针转换的顺序,后跟限定转换(4.4)。
[C++03: 5.19/2]:
其他表达式仅被视为常量表达式,仅用于非本地静态对象初始化(3.6.2)。这些常量表达式应评估为以下之一:
- 空指针值(4.10),
- 空成员指针值(4.11),
- 算术常量表达式
- 地址常量表达式
- 参考常量表达式
- 完整对象类型的地址常量表达式,加上或减去整数常量表达式,或
- 指向成员常量表达式的指针。
这里的关键是标准语言在C ++ 03和C ++ 11之间发生了变化,后者引入了这种形式的空指针常量为文字的要求。
(它们总是需要实际上是常量并且评估为0,因此您可以从测试用例中删除v2
,v3
,w2
和w3
。)
空指针常量可以比通过用户定义的转换更容易转换为double*
,所以......
我相信MSVS正在实施C ++ 03规则。
但是,有趣的是,如果我将GCC置于C ++ 03模式,其行为不会改变,这在技术上是不合规的。我怀疑语言的变化源于当时常见实现的行为,而不是相反。我可以看到一些evidence that GCC was [allegedly] non-conforming in this regard as early as 2004,所以也可能只是标准的措辞变化偶然没有发现GCC错误。