在C++中使用文字0
时,编译器无法消除指针和函数nullptr_t
重载之间的歧义。
说明问题的代码:
struct Bar {};
void foo(Bar*) {
std::cout << "Bar*" << std::endl;
}
void foo(std::nullptr_t) {
std::cout << "nullptr_t" << std::endl;
}
TEST(NullPtrTest, ambiguity) {
foo(nullptr); // OK
foo(0); // ERROR
}
使用Visual Studio 2019:
error C2668: '`anonymous-namespace'::foo': ambiguous call to overloaded function
message : could be 'void `anonymous-namespace'::foo(std::nullptr_t)'
message : or 'void `anonymous-namespace'::foo(`anonymous-namespace'::Bar *)'
message : while trying to match the argument list '(int)'
与GCC 9:
Test.cpp: In member function ‘virtual void {anonymous}::NullPtrTest_ambiguity_Test::TestBody()’:
Test.cpp:425:8: error: call of overloaded ‘foo(int)’ is ambiguous
425 | foo(0); // ERROR
| ^
Test.cpp:415:6: note: candidate: ‘void {anonymous}::foo({anonymous}::Bar*)’
415 | void foo(Bar*) {
| ^~~
Test.cpp:419:6: note: candidate: ‘void {anonymous}::foo(std::nullptr_t)’
419 | void foo(std::nullptr_t) {
| ^~~
解决这个问题的好方法是什么?
我不想做的事:
- 将
0
文字替换为nullptr
。在我们的遗留代码库中有太多的实例 - 添加
int
或long
(或类似(过载。由于0
将是唯一有效的整数值,因此您必须添加一个运行时检查,这会很难看 - 删除
nullptr_t
重载,并在运行时检查指针重载中的值。这是有效的,并不可怕,但它阻止了我们对null常量进行优化实现
谢谢!
澄清:
- 我们使用的是C++14。然而,这些函数被C++11之前的许多代码所使用
- 特定用例是在使用
nullptr
或0
时提供优化的constexpr
实现。通用指针实现仍然需要检查空值。nullptr_t
过载并不是真的必要,但它会很好 - 我主要是想看看是否有我没有考虑的选择
模板不在排除列表中-将其作为模板,限制为仅接受Bar*
:
#include <iostream>
#include <type_traits>
struct Bar {};
template <typename T>
typename std::enable_if<std::is_same_v<T, Bar>>::type foo(T*) {
std::cout << "Bar*n";
}
void foo(std::nullptr_t) {
std::cout << "nullptr_tn";
}
int main() {
Bar b;
foo(nullptr);
foo(0);
foo(&b);
}
我想在实践中,您的Bar*
可能会被传递派生类型,也就是说,您可能需要std::enable_if
中的不同条件。如果foo(Bar*)
在某种程度上是实质性的,那么您可能不希望在标头中实现。但是,由于只有一个实例化,所以您可以将实现放入.cpp
文件中,并显式实例化它,或者将其分派给实现函数。