5

I've written this code to check if a class type have begin function.

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;
}

Which produces 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 anyone please explain why call is ambiguous (even though first check function with signature one check(int (X::*)() = &HasfuncBegin<T>::begin); has default argument to be used) and also how to make my code work?

Edit:

So here is 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};
};
Angelus Mortis
  • 1,534
  • 11
  • 25

2 Answers2

3

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

A 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 about ambiguity.

You haven't actually described what you are trying to achieve with this code (particularly the initialisation of the enum), but are somehow expecting we will work out what you are trying to do. The obvious way to get it to compile would be to remove one of the overloads.

But, unless you describe what you are really trying to achieve, nobody can advise you. Reading sites like this does not grant people mindreading powers.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • What I'm trying to achieve is written in the very first line of question : `I've written this code to check if a class type have begin function.` – Angelus Mortis Dec 26 '15 at 13:26
  • And that description is insufficient. Sorry. – Peter Dec 26 '15 at 13:29
  • @Peter Isn't that clear from the code? If a `begin()` member function exists, `yes` in the enum is supposed to be true. Depending on the overload resolution one or the other `check()` is chosen, leading to a different value for `yes` (doesn't work, but that was the idea). – Peter - Reinstate Monica Dec 26 '15 at 13:47
0

The call is ambiguous because overload selection is based on conversion sequences from the call arguments to the function parameters. The rules are a bit complex 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 ex1(1) call is well-formed. There is a single argument which has a better implicit conversion sequence to v1 than v2 (exact match vs. ellipsis conversion).

The ex2() call is ill-formed. There are no arguments with which to compare conversion sequences and both overloads can be called with no arguments. This is analogous to your code.


It looks like you're stuck with C++03, so here's a possible solution using 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

Community
  • 1
  • 1
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Can you correct my code using fallback mechanism which I was trying to use ? Thanks ) – Angelus Mortis Jan 05 '16 at 10:47
  • I don't entirely understand what the fallback's for. Are you planning on inheriting from `HasfuncBegin` so that you get the default if the class you specify doesn't have one? – TartanLlama Jan 05 '16 at 10:50
  • BTW the comment in original question : "Also you're not doing SFINAE because there's no template argument being deduced" makes sense to you sir? Thanks – Angelus Mortis Jan 05 '16 at 12:21
  • Yeah, it's because you were relying on a default argument, which this doesn't do. – TartanLlama Jan 05 '16 at 12:23
  • But see example http://cpp.sh/4jix , there is no argument deduction going on, I explicitly specified all the template argument. which proves there is no argument deduction going on. Though as you specified argument conversion/binding is going on which was producing error in my original code. – Angelus Mortis Jan 05 '16 at 12:26
  • Whoops, I was being silly. You don't need template deduction for SFINAE, just certain types of invalid constructs in certain contexts. – TartanLlama Jan 05 '16 at 12:30