Reputation: 2277
Relevant code:
#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;
}
This code compiles fine, but when I replace that 1
by a 0
I get (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(...);
^
Why is it that suddenly this call is ambiguous? As far as I know 0
is still an int (and GCC 4.8.2 seems to agree with me, but won't compile it either):
xxx: error: call of overloaded ‘test(std::nullptr_t, int)’ is ambiguous
EDIT:
Please note I'm not asking how to resolve this ambiguity, but why there is an ambiguity when I use 0
instead of 1
.
See this live example for the full code showing the error.
Upvotes: 1
Views: 261
Reputation: 101
In some C++ compilers (most? I'm not sure), nullptr is defined as 0. If you're using managed pointers, then nullptr and 0 are defined differently. But for native pointers, they're the same thing.
So your code is hitting this literal 0, and the compiler suddenly doesn't know whether that's supposed to be a pointer or an integer, because technically it could be either. Whereas "1" is defined in C++ to be a literal int, so there's no confusion.
Unfortunately, I don't see any way around it other than a cast to explicitly tell the compiler what 0 is supposed to mean.
Upvotes: 0
Reputation: 303347
I don't really know what your C
and D
are, but basically you have these two overloads:
static std::true_type test(X*, long);
static std::true_type test(Y*, int*);
test(nullptr, 0);
nullptr
matches both of the first arguments, and 0
matches both of the second. Both overloads are viable, and neither is a better candidate than the other. Hence:
error: call of overloaded
‘test(std::nullptr_t, int)’
is ambiguous
The reason that 0
can pass for int*
is because, from [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.
Upvotes: 1