Why is overload resolution ambiguous in this case?

I wrote this code to check if the class type is begin .

 struct foo //a simple type to check { int begin(){ return 0;} }; struct Fallback { int begin(){ return 0;} }; template<typename T> struct HasfuncBegin : T,Fallback { typedef char one; typedef int two; template<typename X> static one check(int (X::*)() = &HasfuncBegin<T>::begin); template<typename X> static two check(...); enum :bool {yes = sizeof(check<T>())==1, no= !yes}; }; int main() { std::cout<< HasfuncBegin<foo>::yes; return 0; } 

What causes the error:

 error: call of overloaded 'check()' is ambiguous enum {yes = sizeof(check<T>())==1, no= !yes}; ^ C:\XXX\main.cpp:24:16: note: candidate: static HasfuncBegin<T>::one HasfuncBegin<T>::check(int (X::*)()) [with X = foo; T = foo; HasfuncBegin<T>::one = char] static one check(int (X::*)() = &HasfuncBegin<T>::begin); ^ C:\XXX\main.cpp:26:16: note: candidate: static HasfuncBegin<T>::two HasfuncBegin<T>::check(...) [with X = foo; T = foo; HasfuncBegin<T>::two = int] static two check(...); ^ 

Can someone explain why the call is ambiguous (although the first check function with the signature one check(int (X::*)() = &HasfuncBegin<T>::begin); has a default argument to use) and also how to force mine Does the code work ?

Edit:

So here is the final working code:

 struct foo { int begin(){ return 0;} }; struct Fallback { int begin(){ return 0;} }; template<typename T, T ptr> struct dummy{}; template<typename T> struct HasfuncBegin : T,Fallback { typedef char one; typedef int two; template<typename X> static one check(dummy<int (X::*)(),&HasfuncBegin<X>::begin>*); // even this won't work, so replace above statement with below commented one // static one check(dummy<decltype(&HasfuncBegin<X>::begin),&HasfuncBegin<X>::begin>*); template<typename X> static two check(...); enum {yes = sizeof(check<T>(0))==1, no= !yes}; }; 
+5
source share
2 answers

The call is ambiguous because the choice of overload is based on the conversion sequences from the arguments of the call to the parameters of the function. The rules are a bit complicated to fully explain here, but consider these two examples:

 void ex1(int) {} //v1 void ex1(...) {} //v2 void ex2(int = 1) {} //v1 void ex2(...) {} //v2 int main() { ex1(1); ex2(); } 

The call ex1(1) correctly formed. There is one argument that has a better implicit conversion sequence to v1 than v2 (exact match or ellipsis conversion).

The call to ex2() poorly formed. There are no arguments to compare conversion sequences, and both overloads can be called without arguments. This is similar to your code.


It looks like you are stuck with C ++ 03, so here you can find a solution with this answer :

 template<typename T> struct HasfuncBegin { typedef char yes[1]; typedef char no [2]; template <typename U, U> struct type_check; template <typename _1> static yes &chk(type_check<int (T::*)(), &_1::begin > *); template <typename > static no &chk(...); static bool const value = sizeof(chk<T>(0)) == sizeof(yes); }; 

Live demo

0
source

The reason for the ambiguity is that both (templated) overloads of check() are valid matches for check<T>() . You may think that one of them is more important than the other, but the rules of the language are that they are both the same.

The variable argument function ( ... ) is a match for zero or more arguments (i.e. check<T>() ). A function with a single argument that has a default value can match check<T>() .

Hence the message of ambiguity.

You really did not describe what you are trying to achieve with this code (in particular, enum initialization), but somehow expect us to develop what you are trying to do. An obvious way to compile it would be to remove one of the overloads.

But, if you do not talk about what you are really trying to achieve, no one can advise you. Reading sites like this does not give people the opportunity to understand.

+3
source

Source: https://habr.com/ru/post/1239245/


All Articles