MSVC函数与const枚举值0匹配

Bru*_*ine 6 c++ visual-c++

我被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;
}
Run Code Online (Sandbox Code Playgroud)

我希望所有7次测试调用都匹配第一次重载,GCC(实例)和Clang(实例)按预期匹配:

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
Run Code Online (Sandbox Code Playgroud)

但MSVC(实例)将案例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
Run Code Online (Sandbox Code Playgroud)

对于具有(偶然)值0的const枚举变量,似乎MSVC首选转换为指针.我本来期望这种行为的文字为0,但不是枚举变量.

我的问题:MSVC行为符合标准吗?(也许对于旧版本的C++?)如果没有,这是一个已知的扩展还是错误?

Lig*_*ica 5

你没有说出任何标准,但让我们看看它们之间的区别是什么:

[C++11: 4.10/1]:空指针常数是整数表达式(5.19),用于评估至零或类型的prvalue整数类型的prvalue std::nullptr_t.空指针常量可以转换为指针类型; 结果是该类型的空指针值,并且可以与对象指针或函数指针类型的每个其他值区分开.这种转换称为空指针转换.相同类型的两个空指针值应相等.将空指针常量转换为指向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),
  • null成员指针值(4.11),
  • 算术常量表达式,
  • 地址常量表达式,
  • 参考常量表达式,
  • 完整对象类型的地址常量表达式,加上或减去一个整数常量表达式,或
  • 指向成员常量表达式的指针.

这里的关键是标准语言在C++ 03和C++ 11之间发生了变化,后者引入了这种形式的空指针常量是文字的要求.

(他们总是需要实际上是常量和评估为0,这样你就可以删除v2,v3,w2w3从你的测试用例.)

空指针常量可以转换为double*比通过用户定义的转换更容易,因此......

我相信MSVS正在实施C++ 03规则.

但是,有趣的是,如果我将GCC置于C++ 03模式,其行为不会改变,这在技术上是不合规的.我怀疑语言的变化源于当时常见实现的行为,而不是相反.我可以看到一些证据表明海湾合作委员会早在2004年就已经[据称]在这方面不合规,所以也可能只是因为标准的措辞改变了偶然没有发现GCC的错误.