MSVC发现这种方法调用模棱两可,而Clang / GCC则不然吗?

Is MSVC right to find this method call ambiguous, whilst Clang/GCC don't?

本文关键字:GCC Clang 发现 方法 调用 模棱两可 MSVC      更新时间:2023-10-16

Clang (3.9.1) 和 GCC(7,快照)在运行此代码时将"1"、"2"打印到控制台。

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

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

source_file.cpp(9):注意:可能是"无效字典::set(int64_t)">

source_file.cpp(8):注意:或"无效字典::设置(常量字符*)">

source_file.cpp(15):注意:在尝试匹配参数列表"(常量无符号整数)"时

#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);
}

我认为 MSVC 是对的 -ProtocolMajorVersion的值是0,可以是NULL的,也可以是int64_t(0)的。

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

dict.set(ProtocolMinorVersion)

dict.set(0);

source_file.cpp:15:10: 错误:对成员函数"set"的调用不明确 字典集(0);

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

void set(const char *Str) { std::cout << "1"; }

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

void set(int64_t val) { std::cout << "2"; }

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

在 C++11 及之前版本中,任何计算结果为 0 的整数常量表达式都被视为空指针常量这在 C++14 中受到限制:仅考虑值为 0 的整数文本。此外,自 C++11 以来,类型std::nullptr_t的 prvalues 是空指针常量。参见 [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;
}

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

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

当两个编译器同意而一个不同意时,几乎总是那个不同意

的编译器是错误的。我认为,如果您将一个值声明为const unsigned somename = 0;,它不再是一个简单的零,它是一个值为零的命名无符号常量。所以不应该认为等同于指针类型,只留下一个合理的候选者。

话虽如此,set两个函数都需要转换(它不是uint64_t,也不是const char *),所以有人可能会争辩说 MSVC 是正确的 [编译器应选择需要最少转换的类型,如果多个类型需要相等的转换量,这是模棱两可的] - 尽管我仍然认为编译器不应该接受值零的命名常量作为等价于指针......

抱歉,可能更像是"评论"而不是答案 - 我开始写作的目的是说"gcc/clang 是对的",但后来更多地思考得出的结论是"虽然我会对这种行为更满意,但不清楚这是正确的行为"。