c++ - 无法推导成员函数包装器的返回类型

标签 c++ c++14 wrapper variadic-templates template-argument-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 的包装器方法并没有为我提供返回类型供以后使用。 我尝试了很多变体 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;
}

我期待找到一个解决方案,其中 Fn 的签名与 Args...被正确识别,因为 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... )格式正确,但我不知道正确的格式应该是什么样子。

任何帮助将不胜感激

最佳答案

你不需要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 询问

but could you perhaps clarify why two separate parameter sets are needed in your first solution?

两个单独的参数集不是严格要求的,但这提供了更大的灵 active 。

假设你有一个集合

  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) 

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

foo(),编译器将 As... 推断为 long, long

1, 2,编译器将 As... 推断为 int, int

所以:编译错误,因为编译器有推导冲突。

使用双类型集(As1...As2...),编译器将As1... 推断为long, longAs2... 作为 int, int。没有冲突,所以没有编译错误。

并且使用 int 值调用 foo() 也没有问题,因为 int 被转换为 longs.

关于c++ - 无法推导成员函数包装器的返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54447141/

相关文章:

成员函数的 C++ 模板特化

c++ - initializer_list 不可变的性质导致过度复制

c++ - 如何在现代 C++ 中基于另一组可变参数模板参数来表示可变参数模板类型?

python - 将 C++ 类指针的向量发送到 python

c++ - 不同容器的包装器 contains() 函数

c++ - 我是否需要 1 个读者和 1 个作者的互斥锁,我不介意丢失一些写入?

c++ - 函数尝试 block 和 noexcept

C++:通用函数包装类作为非模板类​​的成员

java - 测试两个原始包装数字是否代表相等的原始数字

C++ zilib deflate/inflate 和 ztream 参数