传递 0 而不是 1 时 sfinae 模棱两可的调用

sfinae ambiguous call when passing 0 instead of 1

相关代码:

#include <iostream>
#include <type_traits>

template<template<typename...> class C, typename... T>
struct is_valid_instantiation_impl
{
  // Default constructor
  template<template<typename...> class D>
  static std::true_type test(decltype(D<T...>{ })*, int);
  // Copy constructor
  template<template<typename...> class D>
  static std::true_type test(decltype(D<T...>{ std::declval<const D<T...>&>() })*, long);
  // Move constructor
  template<template<typename...> class D>
  static std::true_type test(decltype(D<T...>{ std::declval<D<T...>&&>() })*, int*);

  template<template<typename...> class D>
  static std::false_type test(...);

  using type =  decltype(test<C>(nullptr, 1));
  //                                      ^ this one
};

template<template<typename...> class C, typename... T>
struct is_valid_instantiation : is_valid_instantiation_impl<C, T...>::type { };

template<typename>
struct tester;

template<>
struct tester<int>
{
  tester(int);
};

int main(int argc, char** argv)
{
  std::cout << "instantiable<int>: " << is_valid_instantiation<tester, int>::value << std::endl;
}

这段代码编译得很好,但是当我用 0 替换 1 时,我得到 (Clang 3.4)

xxx: error: call to 'test' is ambiguous
  using type =  decltype(test<C>(nullptr, 0));
                         ^~~~~~~
xxx: note: in instantiation of template class 'is_valid_instantiation_impl<tester, int>' requested here
...

xxx: note: candidate function [with D = tester]
  static std::true_type test(decltype(D<T...>{ std::declval<const D<T...>&>() })*, long);
                        ^
xxx: note: candidate function [with D = tester]
  static std::true_type test(decltype(D<T...>{ std::declval<D<T...>&&>() })*, int*);
                        ^
xxx: note: candidate function [with D = tester]
  static std::false_type test(...);
                         ^

为什么这个调用突然有歧义了?据我所知 0 仍然是一个 int ( 和 GCC 4.8.2 似乎同意我的看法,但也不会编译它):

xxx: error: call of overloaded ‘test(std::nullptr_t, int)’ is ambiguous

编辑:

请注意,我不是在问如何解决这种歧义,而是在使用 0 而不是 1 时为什么会出现歧义。 有关显示错误的完整代码,请参阅 this live example

我真的不知道你的 CD 是什么,但基本上你有这两个重载:

static std::true_type test(X*, long);
static std::true_type test(Y*, int*);

test(nullptr, 0);

nullptr 匹配第一个参数,0 匹配第二个参数。两种重载都是可行的,并且没有一个比另一个更好。因此:

error: call of overloaded ‘test(std::nullptr_t, int)’ is ambiguous

0 可以通过 int* 的原因是,来自 [conv.ptr]:

A null pointer constant is an integer literal with value zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type.

在某些 C++ 编译器(大多数?我不确定)中,nullptr 被定义为 0。如果您使用托管指针,则 nullptr 和 0 的定义不同。但是对于本机指针,它们是同一回事。

所以你的代码命中了这个字面值 0,编译器突然不知道它应该是一个指针还是一个整数,因为从技术上讲它可能是。而“1”在 C++ 中定义为字面量 int,因此不会造成混淆。

不幸的是,除了强制转换以明确告诉编译器 0 应该是什么意思之外,我看不到任何解决方法。