Question

I want to automatically choose the right pointer-to-member among overloaded ones based on the "type" of the member, by removing specializations that accept unconcerned 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'm expecting it to choose the right function between all possible. The problem is that the compiler says "cannot deduce template argument as function argument is ambiguous".

It seems I don't know how to use enable_if, because if so the compiler would only allow the specialization if the specified function has the right type...

Note that I want to have C++03 solutions (if possible) - my code must compile on some old compilers.

Thanks in advance

Was it helpful?

Solution

You can never refer to an overloaded function without disambiguating it (means: static_casting it to the correct type). When you instantiate Helper::func the type of the function argument cannot be known without ever disambiguating it.

OTHER TIPS

The reason it doesn't compile is quite simply that there are several different overloaded functions and it doesn't know which one you mean. Granted, only one of these (void set(int,int)) would actually compile, given the specialization Helper<test,INT_2>. However, this is not enough for the compiler to go on.

One way of getting this to compile would be to explicitly cast &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));

Either way, you need to let the compiler know which of the set functions you are trying to refer to.

EDIT:

As I understand it, you want to be able to deduce, from the use of a Type, which function type 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 EDIT:

A thought just occurred to me. In this special case which you've done, where U is SetterOK::setter_type, things can be simplified further by completely removing the template arguments for func:

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

This would make the init method a simpler:

void init()
{
  Helper<test,INT_2>::func(&test::set);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top