推断出仿函数的返回类型的通用方法?

时间:2012-08-20 07:18:00

标签: c++ templates lambda c++11

这个问题是How to deduce the type of the functor's return value?的后续问题 我正在以更抽象的方式重新制定它。

给出模板函数的伪代码

template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(<decl-expr>)
{
// do something
// ............
return fn(<ret-expr>)
}

其中<ret-expr>是涉及arg的任意表达式,我应该使用<decl-expr>来设置ComputeSomething的返回类型等于函数的返回类型

仿函数可以是类,lambda或函数指针。

到目前为止我找到的部分解决方案。

(a)ecatmur所做的关联问题的答案。基本上,它在<decl-expr>中重复return语句。问题:它容易出错,如果包含局部变量则不起作用。

(b)它仅适用于函数指针

template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, Ret(*fn)(Arg))

(c)它假定仿函数的参数是Arg类型(一般可能不成立),并且Arg需要默认构造

template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(Arg())

(d)使用std::declval,它应该解除默认构造限制,如how to deduce the return type of a function in template中所述。有谁可以解释一下它是如何工作的?

template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(std::declval<Arg>())

5 个答案:

答案 0 :(得分:11)

使用result_of。它向后兼容并从代码中消除所有丑陋declval的痛苦。如果您实际上只是转发值,则仍需要记住添加右值引用限定符(&&)。

我认为重要的其他内容:您的函数将参数转发给另一个函数。在这种情况下,您应该始终使用rvalue引用来传递参数。

如果您要做的就是提高可维护性:在RETURNS宏周围尝试多次尝试最小化返回类型声明和实际返回表达式之间的重复,但我还没有看到允许函数体包含多于实际返回语句的任何函数体。

至于declval如何工作:它依赖于编译器。不允许在评估的内容中出现,并且其参数可以是不完整的类型。见20.2.4

答案 1 :(得分:11)

std::declval是一个仅声明(未定义)的函数模板。因此,它只能用于未评估的上下文,例如sizeofdecltype的参数。声明它返回指定类型的右值。这允许您使用它为decltype表达式中的函数调用生成伪参数。

e.g。

typedef decltype(fn(std::declval<Arg>())) t;

声明t是调用fn的结果的类型,其值为Arg的右值。这类似于您的情况(c)(fn(Arg())),但它不需要任何Arg,因此它适用于没有默认构造函数的类型。

如果您的返回表达式使用foo类型的局部变量,那么无论您如何构造decltype(fn(std::declval<foo>())),都可以再次使用foo

如果您需要左值,例如命名对象或左值引用,则可以使用std::declval<foo&>()。这允许您处理类型取决于您是否具有左值或右值的情况。

答案 2 :(得分:5)

这是我自己的解决方案,我能得到的最好的

template <typename Arg, typename Fn>
typename std::result_of<Fn(Arg)>::type ComputeSomething(Arg arg, Fn fn)

答案 3 :(得分:3)

要使(c)适用于任何事情,您需要2次重载。第一个如(c)所示,第二个:

template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, std::function<Ret(Arg)> fn)

此外,正如gcc bug 54111所示 - 扣除退货类型非常不可靠。

答案 4 :(得分:2)

(b)不仅与函数指针一起工作的变体应该类似于

template<typename Arg, typename Ret>
Ret ComputeSomething (Arg arg, function<auto (Arg) -> Ret> f)