我有一组具有不同签名的方法,它们都需要相同的前缀和后缀代码,因此我想将它们中的每一个都整齐地包装起来。
我正在尝试为我的成员函数的通用包装器制定一个 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_of
和 decltype()
使用以下代码,但我无法正确编译它
#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, long
和 As2...
作为 int, int
。没有冲突,所以没有编译错误。
并且使用 int
值调用 foo()
也没有问题,因为 int
被转换为 long
s.
关于c++ - 无法推导成员函数包装器的返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54447141/