SFINAE: ambiguous overload if called with no arguments
c++, c++11, sfinae
Solution
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]:
- If there are m arguments in the list, all candidate functions having exactly m parameters are viable.
- A candidate function having fewer than m parameters is viable only if it has an ellipsis in its parameter list (8.3.5). For the purposes of overload resolution, any argument for which there is no corresponding parameter is considered to “match the ellipsis” (13.3.3.1.3) .
- A candidate function having more than m parameters is viable only if the (m+1)-st parameter has a default argument (8.3.6). For the purposes of overload resolution, the parameter list is truncated on the right, so that there are exactly m parameters.
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:
template<> constexpr bool has_begin_member<vector<int>>(...);
template<> constexpr bool has_begin_member<vector<int>>();
Which is obviously ambiguous, just like this is:
int f(...);
int f();
int i = f(); // error
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.
Problem
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> >] ```