Perform overload resolution with template meta-pro

2020-07-22 16:45发布

问题:

Inspired by another question I tried to find a way to deduce the type of an overload member function given the actual argument used to call that function. Here is what I have so far:

#include <type_traits>

template<typename F, typename Arg>
struct mem_fun_type {
  // perform overload resolution here
  typedef decltype(std::declval<F>()(std::declval<Arg>())) result_type;
  typedef decltype(static_cast<result_type (F::*)(Arg)>(&F::operator())) type;
};

struct foo {};

struct takes_two
{
  void operator()(int);
  void operator()(foo);
};

struct take_one {
  void operator()(float);
};

int main()
{
  static_assert(std::is_same<mem_fun_type<take_one, float>::type, 
                             void (take_one::*)(float)>::value, "Zonk");
  static_assert(std::is_same<mem_fun_type<takes_two, double>::type, 
                             void (takes_two::*)(float)>::value, "Zonk");
  return 0;
}

As long as the template parameter Arg matches the actual type the static_cast will succeed, but this is only the most trivial case of overload resolution (exact match). Is it possible to perform the complete overload resolution process in template metaprogramming?

This is purely hypothetical and not intended for real-world use.

回答1:

This is closest I came to it so far: define function returning tables of different sizes and your result is sizeof(select(...)) receiving pointer to function you want to match. To ensure that the code will compile even if function does not exist in given class, you can use separate check has_function.

Result of overload resolution is in select<has_function<T>::value, T>::value.

With this code you can even "resolve" data members, not just functions, it's only a question of making right parameter for select function.

However there is one deficiency here - overload resolution is not on function parameters, but on function type. Meaning none of usual parameter type conversions takes place.

  // Verify the name is valid
  template <typename T>
  struct has_function
  {
    struct F {int function;};
    struct D : T, F {};
    template <typename U, U> struct same_;
    template <typename C> static char(&select_(same_<int F::*, &C::function>*))[1];
    template <typename> static char(&select_(...))[2];
    enum {value = sizeof(select_<D>(0)) == 2};
  };

  // Values to report overload results
  enum type { none=1 , function_sz_size_t , function_sz , function_string };

  template <bool, typename R> struct select;

  template <typename R> struct select<false, R>
  {
    enum {value = none};
  };

  template <typename R> struct select<true, R>
  {
    // Define your overloads here, they don't have to be templates.
    template <typename Ret, typename Arg> static char(&select_(Ret (R::*)(const char*, Arg)))[function_sz_size_t];
    template <typename Ret, typename Arg> static char(&select_(Ret (R::*)(Arg)))[function_sz];
    template <typename Ret> static char(&select_(Ret (R::*)(std::string)))[function_string];
    template <typename Ret> static char(&select_(Ret (R::*)(std::string&&)))[function_string];
    template <typename Ret> static char(&select_(Ret (R::*)(const std::string&)))[function_string];
    static char(&select_(...))[none];
    enum {value = sizeof(select_(&R::function))};
  };