Consider the following typical SFINAE test function (it checks if a type has a begin() member function)
template <class> constexpr bool
has_begin_member (...) { return false; }
template <class T> constexpr bool
has_begin_member (decltype (std::declval <T>().begin ())* = 0) {
return true;
}
I can call it with an argument:
has_begin_member <int> (0); // yields false
but without any arguments:
has_begin_member <int> (); // compilation error
it leads to the following ambiguity:
error: call of overloaded 'has_begin_member()' is ambiguous
note: candidates are:
note: constexpr bool has_begin_member(...)
note: constexpr bool has_begin_member(decltype (declval<T>().begin())*)
Why doesn’t the “ellipsis trick” work in that case?
Edit: full program:
#include <utility>
#include <vector>
template <class> constexpr bool
has_begin_member (...) { return false; }
template <class T> constexpr bool
has_begin_member (decltype (std::declval <T>().begin ())* = 0) {
return true;
}
static_assert (!has_begin_member <int> (0), "broken");
static_assert (has_begin_member <std::vector <int>> (0), "broken");
static_assert (!has_begin_member <int> (), "broken");
static_assert (has_begin_member <std::vector <int>> (), "broken");
int
main (){}
Compilation:
g++ -std=c++11 -o toto ./toto.cpp
./toto.cpp:17:58: error: call of overloaded 'has_begin_member()' is ambiguous
./toto.cpp:17:58: note: candidates are:
./toto.cpp:5:5: note: constexpr bool has_begin_member(...) [with <template-parameter-1-1> = std::vector<int>]
./toto.cpp:8:5: note: constexpr bool has_begin_member(decltype (declval<T>().begin())*) [with T = std::vector<int>; decltype (declval<T>().begin()) = __gnu_cxx::__normal_iterator<int*, std::vector<int> >]
For the
has_begin_member<int>()case the second overload is not viable because template argument substitution fails, so only the first overload is viable, so no ambiguity.For the
has_begin_member<std::vector<int>>()case substitution succeeds so there are two viable functions.13.3.2 [over.match.viable]:
In this case m is zero, the first overload is viable (by the second bullet) and the second overload is also viable (by the third bullet) but for the purposes of overload resolution the parameter with a default argument is ignored, and so the best viable functions is found by comparing:
Which is obviously ambiguous, just like this is:
There is no conversion sequence needed to call either function, so they cannot be ranked in terms of which has a “better conversion sequence” than the other (using the rules in 13.3.3.2 [over.ics.rank],) which means they are ambiguous.