传递 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。
我真的不知道你的 C
和 D
是什么,但基本上你有这两个重载:
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 应该是什么意思之外,我看不到任何解决方法。
相关代码:
#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。
我真的不知道你的 C
和 D
是什么,但基本上你有这两个重载:
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 应该是什么意思之外,我看不到任何解决方法。