推论成员函数包装的返回类型问题

时间:2019-01-30 18:24:16

标签: c++ c++14 wrapper variadic-templates template-deduction

我有一组具有不同签名的方法,它们都需要相同的前缀和后缀代码,因此我想将它们每个都整齐地包装起来。 我正在尝试为成员函数的通用包装制定C ++ 14可变参数模板,并且在推导返回类型时遇到麻烦。 auto可以工作,但是我需要显式的返回类型以提供有效的返回值,即使我在执行包装时在包装器内捕获到异常。

到目前为止,我设法使用std::result_of推导出普通包装函数的返回类型,并且使用auto对非静态成员函数得到了正确的行为。我现在尝试了两天,以使std::result_of方法适用于成员函数,还尝试了decltype()std::declval()的许多变体,但到目前为止还没有运气。我没有关于如何推断返回类型的想法。

这是我的工作示例

#include <iostream>

int foo(int a, int b) { return a + b; }
int bar(char a, char b, char c) { return a + b * c; }


template<typename Fn, typename... Args>
typename std::result_of<Fn&(Args... )>::type 
wrapFunc(Fn f, Args... args) {
  //Do some prefix ops
  typename std::result_of<Fn&(Args... )>::type ret = f(std::forward<Args>(args)...);
  //Do some suffix ops
  return ret;
}

class MemberWithAuto {
  private:
  public:
    MemberWithAuto() {};
    int foo(int i, int j) { return i + j;}

    template<typename Fn, typename... Args>
    auto wrapper(Fn f, Args... args) {
      //Do some prefix ops
      auto ret = (*this.*f)(std::forward<Args>(args)...);
      //Do some suffix ops
      return ret;
    } 
};



int main() {

  std::cout << "FuncWrapper for foo with 1 + 2 returns "         << wrapFunc<decltype(foo)>(foo, 1, 2)      << std::endl;
  std::cout << "FuncWrapper for bar with 'a' + 'b' * 1  returns "  << wrapFunc<decltype(bar)>(bar, 'a','b', 1)  << std::endl;

  MemberWithAuto meau = MemberWithAuto();
  std::cout << "MemberFunction with Auto with 6 + 1 returns  "  << meau.wrapper(&MemberWithAuto::foo, 6, 1)  << std::endl;

  return 0;
}

这两种方法都能很好地工作,但是使用auto的wrapper方法并不能为我提供返回类型以供以后使用。 我使用以下代码对std::result_ofdecltype()进行了多种尝试,但无法正确编译

#include <iostream>

int foo(int a, int b) { return a + b; }
int bar(char a, char b, char c) { return a + b * c; }

class MemberWithDecl {
  private:
  public:
    MemberWithDecl() {};
    int foo(int i, int j) { return i + j;}

    template<typename Fn, typename... Args>
    typename std::result_of<Fn&(Args... )>::type wrapper(Fn f, Args... args) {
      //Do some prefix ops
      typename std::result_of<Fn&(Args... )>::type ret = (*this.*f)(std::forward<Args>(args)...);
      //Do some suffix ops
      return ret;
    } 
};



int main() {
  MemberWithDecl medcl = MemberWithDecl();
  std::cout << "MemberFunction with declaration also works "  << medcl.wrapper(&MemberWithDecl::foo, 6, 1)  << std::endl;
  return 0;
}

我期望找到一种解决方案,其中可以正确识别FnArgs...的签名,因为auto也可以成功推断出类型。我的类型声明似乎没有找到匹配的模板,无论我尝试了什么变化,我都能得到

error: no matching function for call to ‘MemberWithDecl::wrapper(int (MemberWithDecl::*)(int, int), int, int)’

如果我将包装器的返回类型设置为auto,然后尝试在其中声明变量ret,我将得到

error: no type named ‘type’ in ‘class std::result_of<int (MemberWithDecl::*&(int, int))(int, int)>’
       typename std::result_of<Fn&(Args... )>::type ret = (*this.*f)(std::forward<Args>(args)...);

阅读标准后,我认为这意味着result_of并不认为Fn&(Args... )格式正确,但是我不知道正确的格式应该是什么样。

任何帮助将不胜感激

1 个答案:

答案 0 :(得分:1)

您不需要std::result_of;你可以简单地写

  template <typename R, typename ... As1, typename ... As2>
  R wrapper (R(MemberWithDecl::*fn)(As1...), As2 ... args)
   { return (*this.*fn)(std::forward<As2>(args)...); } 

如果您确实要使用std::result_of,则必须添加一个MemberWithDecl指针(类型为this)作为第一个参数。

  template <typename Fn, typename ... Args>
  typename std::result_of<Fn&(MemberWithDecl*, Args... )>::type
        wrapper (Fn f, Args ... args)
   {
     typename std::result_of<Fn&(MemberWithDecl*, Args... )>::type ret
        = (*this.*f)(std::forward<Args>(args)...);

     return ret;
   } 

-编辑-

OP询问

  

但是您也许可以澄清一下,为什么第一个解决方案需要两个单独的参数集?

并非严格要求两个单独的参数集,但这可以提供更大的灵活性。

假设您有一套

  template <typename R, typename ... As>
  R wrapper (R(MemberWithDecl::*fn)(As...), As ... args)
   { return (*this.*fn)(std::forward<As>(args)...); } 

并给出

 long foo (long a, long b) { return a + b; }

假设您致电

 wrapFunc(foo, 1, 2) 

现在,编译器必须从As...foo()推论1, 2

编译器从foo()推导As...long, long

编译器从1, 2推导As...int, int

所以:编译错误是因为编译器存在推论冲突。

使用双重类型(As1...As2...),编译器将As1...推导为long, long,将As2...推导为int, int。没有冲突,因此没有编译错误。

使用foo()值调用int也没有问题,因为int被转换为long