MSVC 认为此方法调用不明确是否正确,而 Clang/GCC 不正确?

H B*_*amy 3 c++ gcc clang visual-c++ language-lawyer

运行此代码时,Clang (3.9.1) 和 GCC (7, snapshot) 将“1”、“2”打印到控制台。

但是,MSVC 无法编译此代码:

source_file.cpp(15):错误 C2668:'Dictionary::set':对重载函数的调用不明确

source_file.cpp(9):注意:可能是'void Dictionary::set(int64_t)'

source_file.cpp(8): 注意:或 'void Dictionary::set(const char *)'

source_file.cpp(15): 注意:在尝试匹配参数列表时 '(const unsigned int)'

#include <iostream>

static const unsigned ProtocolMajorVersion = 1;
static const unsigned ProtocolMinorVersion = 0;

class Dictionary {
public:
    void set(const char *Str) { std::cout << "1"; }
    void set(int64_t val) { std::cout << "2"; }
};

int main() {
    Dictionary dict;
    dict.set(ProtocolMajorVersion);
    dict.set(ProtocolMinorVersion);
}
Run Code Online (Sandbox Code Playgroud)

我认为MSVC是正确的-的价值ProtocolMajorVersion是0,它可以是NULL或int64_t(0)。

但是,更换时似乎是这种情况

dict.set(ProtocolMinorVersion)
Run Code Online (Sandbox Code Playgroud)

和

dict.set(0);
Run Code Online (Sandbox Code Playgroud)

source_file.cpp:15:10: 错误:对成员函数“set”的调用不明确 dict.set(0);

source_file.cpp:8:10: 注意:候选函数

void set(const char *Str) { std::cout << "1"; }
Run Code Online (Sandbox Code Playgroud)

source_file.cpp:9:10: 注意:候选函数

void set(int64_t val) { std::cout << "2"; }
Run Code Online (Sandbox Code Playgroud)

那么这里发生了什么 - 哪个编译器是正确的?如果 GCC 和 Clang 都接受不正确的代码,或者 MSVC 只是有问题,我会感到惊讶吗?请参考标准

dyp*_*dyp 5

在 C++11 及之前,任何计算结果为 0 的整型常量表达式都被视为空指针常量。这在 C++14 中受到限制:仅考虑值为 0 的整数文字。此外,std::nullptr_t自 C++11 起,类型的纯右值是空指针常量。参见 [conv.ptr] 和CWG 903。

关于重载解析,积分转换unsigned->int64_t和指针转换空指针常量->const char*都具有相同的等级:转换。参见 [over.ics.scs] / 表 12。

所以如果ProtocolMinorVersion被认为是一个空指针常量,那么调用是不明确的。如果你只是编译下面的程序:

static const unsigned ProtocolMinorVersion = 0;

int main() {
    const char* p = ProtocolMinorVersion;
}
Run Code Online (Sandbox Code Playgroud)

您将看到 clang 和 gcc 拒绝这种转换,而 MSVC 接受它。

由于 CWG 903 被认为是一个缺陷,我认为 clang 和 gcc 是正确的。