我有一组具有不同签名的方法,它们都需要相同的前缀和后缀代码,因此我想将它们每个都整齐地包装起来。
我正在尝试为成员函数的通用包装制定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_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... )
格式正确,但是我不知道正确的格式应该是什么样。
任何帮助将不胜感激
答案 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
。