有没有一种好的方法来解决nullptr_t和指针重载之间的歧义



在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。在我们的遗留代码库中有太多的实例
  • 添加intlong(或类似(过载。由于0将是唯一有效的整数值,因此您必须添加一个运行时检查,这会很难看
  • 删除nullptr_t重载,并在运行时检查指针重载中的值。这是有效的,并不可怕,但它阻止了我们对null常量进行优化实现

谢谢!

澄清:

  • 我们使用的是C++14。然而,这些函数被C++11之前的许多代码所使用
  • 特定用例是在使用nullptr0时提供优化的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文件中,并显式实例化它,或者将其分派给实现函数。