SFINAE: ambiguous overload if called without arguments

Consider the following typical test function, SFINAE (it checks to see if the member function has a type begin() )

  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 this an argument:

 has_begin_member <int> (0); // yields false 

but without any arguments:

 has_begin_member <int> (); // compilation error 

this 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 does the "elliptical trick" not work in this 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> >] 
+4
source share
1 answer

For the case has_begin_member<int>() second overload is not viable because the replacement of the template argument failed, so only the first overload is viable, so there is no ambiguity.

To replace has_begin_member<std::vector<int>>() job is successful, so there are two viable functions.

13.3.2 [over.match.viable]:

  • If there are m arguments in the list, all candidate functions that have exactly m parameters are viable.
  • A candidate function with at most m parameters is viable only if its parameter has an ellipsis list (8.3.5). For the purpose of overload resolution, any argument for which there is no corresponding argument is considered to be "consistent with ellipsis" (13.3.3.1.3).
  • A candidate function with more than m parameters is viable only if the parameter (m + 1) -st has a default argument (8.3.6). For the purpose of overload resolution, the parameter list is truncated to the right, so there are exactly m parameters.

In this case, m is zero, the first overload is viable (the second bullet), and the second overload is also viable (the third brand), but for the purpose of resolving the overload, the parameter with the argument is ignored by default, and therefore the best viable functions are detected by comparison:

 template<> constexpr bool has_begin_member<vector<int>>(...); template<> constexpr bool has_begin_member<vector<int>>(); 

This is clearly ambiguous, as well as:

 int f(...); int f(); int i = f(); // error 

There is no conversion sequence needed to call any of the functions, so they cannot be ranked in terms of which there is a “better conversion sequence” than another (using the rules in 13.3.3.2 [over.ics.rank]); which means they are ambiguous.

+3
source

All Articles