Ambiguous template arguments not excluded by enable_if

I want to automatically select the correct member pointer among the overloaded based on the "type" of the member, removing specializations that accept non-interested members (via enable_if).

I have the following code:

class test;

enum Type
{
    INT_1,
    FLOAT_1,
    UINT_1,
    CHAR_1,
    BOOL_1,
    INT_2,
    FLOAT_2,
    UINT_2,
    CHAR_2,
    BOOL_2
};
template<typename T, Type Et, typename func> struct SetterOk                            { static const bool value = false; };
template<typename T> struct SetterOk<T,INT_1,void (T::*)(int)>                          { static const bool value = true; };
template<typename T> struct SetterOk<T,FLOAT_1,void (T::*)(float)>                      { static const bool value = true; };
template<typename T> struct SetterOk<T,UINT_1,void (T::*)(unsigned int)>                { static const bool value = true; };
template<typename T> struct SetterOk<T,CHAR_1,void (T::*)(char)>                        { static const bool value = true; };
template<typename T> struct SetterOk<T,BOOL_1,void (T::*)(bool)>                        { static const bool value = true; };
template<typename T> struct SetterOk<T,INT_2,void (T::*)(int,int)>                      { static const bool value = true; };
template<typename T> struct SetterOk<T,FLOAT_2,void (T::*)(float,float)>                { static const bool value = true; };
template<typename T> struct SetterOk<T,UINT_2,void (T::*)(unsigned int, unsigned int)>  { static const bool value = true; };
template<typename T> struct SetterOk<T,CHAR_2,void (T::*)(char,char)>                   { static const bool value = true; };
template<typename T> struct SetterOk<T,BOOL_2,void (T::*)(bool,bool)>                   { static const bool value = true; };

template <bool, class T = void> struct enable_if {};
template <class T> struct enable_if<true, T> { typedef T type; };


template<typename T, Type Et>
struct Helper
{
    template<typename U>
    static void func(U method, typename enable_if<SetterOk<T,Et,U>::value>::type* dummy = 0)
    {
    }
};

class test
{
    public:
        void init()
        {
            Helper<test,INT_2>::func(&test::set);
        }

        void set2(int);
        void set(int);
        void set(int,int);
        void set(float,float);
};

int main()
{
    test t;
    t.init();
    return 0;
}

      

I expect him to choose the right function between all the possible ones. The problem is that the compiler says "cannot deduce the template argument because the function argument is ambiguous."

I don't seem to know how to use enable_if, because if it does, then the compiler will only allow specialization if the specified function is of the correct type ...

Please note that I want to have C ++ 03 solutions (if possible) - my code needs to compile on some older compilers.

Thank you in advance

+3


source to share


2 answers


You cannot refer to an overloaded function without ambiguously disambiguating it (means: static_cast

with the correct setup). When you instantiate Helper::func

, the type of the function argument cannot be known without ambiguity.



+1


source


The reason it doesn't compile is simply that there are several different overloaded functions and it doesn't know what you mean. Of course, only one of them (void set (int, int)) will actually compile given the specialization Helper<test,INT_2>

. However, this is not enough for the compiler.

One way to get this to compile is to explicitly point &test::set

to the appropriate type:

Helper<test,INT_2>::func(static_cast<void (test::*)(int,int)>(&test::set));

Another way would be to use explicit template specialization:

Helper<test,INT_2>::func<void (test::*)(int,int)>((&test::set));

In any case, you need to tell the compiler which of the collection functions you are trying to reference.

EDIT:



As I understand it, you want to be able to infer from using a type which type of function should be used. The following alternative achieves this:

template<typename T, Type Et> struct SetterOK{};
template<typename T> struct SetterOK<T,INT_1> {typedef void (T::*setter_type)(int);};
template<typename T> struct SetterOK<T,FLOAT_1> {typedef void (T::*setter_type) (float);};
// ...
template<typename T> struct SetterOK<T,INT_2> {typedef void (T::*setter_type)(int,int);};
// ....

template<typename T, Type Et>
struct Helper
{
  template<typename U>
  static void func(U method)
  {
  }
};

class test
{
public:
  void init()
  {
    Helper<test,INT_2>::func<SetterOK<test,INT_2>::setter_type >(&test::set);
  }

  void set2(int);
  void set(int);
  void set(int,int);
  void set(float,float);
};

int main()
{
  test t;
  t.init();
  return 0;
}

      

ADDITIONAL EDITING:

An idea occurred to me. In this special case, where U is SetterOK :: setter_type, this can all be simplified by completely removing the template arguments for func:

static void func(typename SetterOK<T,Et>::setter_type method)
{
}

      

This will simplify the init method:

void init()
{
  Helper<test,INT_2>::func(&test::set);
}

      

+1


source







All Articles