指向模板成员函数的指针的类型不匹配

时间:2018-07-27 04:41:59

标签: c++11 function-pointers variadic-templates

我正在关注this code snippet,它使将成员函数传递给期望C样式回调的接口(即,接口期望指向回调的函数指针和void*)变得更加容易指向用户数据的指针,这些数据将依次传递给回调)。实际上,我想将Helper::M转换为下面的Helper::V

我正在尝试修改代码段以自动推断模板参数。这是我目前的尝试。

#include <iostream>

template <typename R, typename T, typename... Args>
struct Helper {
  using V = R (*)(void*, Args...);
  using M = R (T::*)(Args...);
  template <M m>
  static R Fn(void* data, Args... args) {
    return (static_cast<T*>(data)->*m)(std::forward<Args...>(args...));
  }
};

template <typename R, typename T, typename... Args>
typename Helper<R, T, Args...>::V Cast(R (T::*m)(Args...)) {
  return Helper<R, T, Args...>::template Fn<m>;
}

int CIntf(void* data, int (*f)(void*, int)) { return f(data, 1); }

struct UserData {
  int x;
  int Add(int y) { return x + y; }
};

int main(int argv, char** argc) {
  UserData data = {4};
  // Explicit parameters; works.
  std::cout << CIntf(&data, Helper<int, UserData, int>::Fn<&UserData::Add>)
            << "\n";
  // Deduced parameters; fails.
  std::cout << CIntf(&data, Cast(&UserData::Add)) << "\n";

  return 0;
}

我尝试使用gcc -std=c++11 -lstdc++进行编译。显式参数方法可以正常工作,但推导参数方法会出现以下错误:

tmp.cc: In instantiation of ‘typename Helper<R, T, Args>::V Cast(R (T::*)(Args ...)) [with R = int; T = UserData; Args = {int}; typename Helper<R, T, Args>::V = int (*)(void*, int)]’:
tmp.cc:30:58:   required from here
tmp.cc:15:42: error: no matches converting function ‘Fn’ to type ‘using V = int (*)(void*, int) {aka int (*)(void*, int)}’
   return Helper<R, T, Args...>::template Fn<m>;
                                          ^~~~~
tmp.cc:8:12: note: candidate is: template<int (UserData::* m)(int)> static R Helper<R, T, Args>::Fn(void*, Args ...) [with R (T::* m)(Args ...) = m; R = int; T = UserData; Args = {int}]
   static R Fn(void* data, Args... args) {

请注意,它可以正确推导模板参数,但是无法将Helper<int, UserData, int>::Fn<m>转换为int (*)(void*, int);为什么?在显式情况下,同样的转换成功(除非m&UserData::Add有所不同)。

2 个答案:

答案 0 :(得分:1)

不幸的是,您必须为此使用宏:

#define makeFunc(method) &Helper<decltype(method)>::Fn<method>

然后像这样重新定义您的助手,使其起作用:

template <typename T>
struct Helper;

template <typename R, typename T, typename... Args>
struct Helper<R(T::*)(Args...)>

之所以不能使用演绎,是因为演绎仅适用于运行时值的函数参数。并且您需要使用方法的地址作为模板参数,该参数应为编译时值。

因此,当您这样做时:

return Helper<R, T, Args...>::template Fn<m>;

您正在传递运行时值m作为模板参数,这是不可能的。

答案 1 :(得分:0)

作为参考,这是使用宏的完整代码。还要注意,在原始代码中使用std::forward对于多个参数(see this answer)是不正确的。

#include <iostream>
#include <utility>

template <typename T>
struct Helper;

template <typename R, typename T, typename... Args>
struct Helper<R (T::*)(Args...)> {
  template <R (T::*m)(Args...)>
  static R Fn(void* t, Args... args) {
    return (static_cast<T*>(t)->*m)(std::forward<Args>(args)...);
  }
};

#define VOID_CAST(m) &Helper<decltype(m)>::Fn<m>

struct UserData {
  int x;
  int Add1(int y) { return x + y; }
  int Add2(int y, int z) { return x + y + z; }
};

int Call1(void* data, int (*f)(void*, int)) { return (*f)(data, 1); }
int Call2(void* data, int (*f)(void*, int, int)) { return (*f)(data, 1, 2); }

int main() {
  UserData data = {4};
  std::cout << Call1(&data, VOID_CAST(&UserData::Add1)) << "\n";
  std::cout << Call2(&data, VOID_CAST(&UserData::Add2)) << "\n";
  return 0;
}