推导函子返回类型的泛型方法

Generic way to deduce the return type of a functor?

本文关键字:泛型方法 返回类型      更新时间:2023-10-16

这个问题是如何推导函子返回值的类型的后续问题?我正在用一种更抽象的方式重新表述它。

给定模板函数的伪代码

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,它应该解除默认的可构造性限制,正如在如何推导模板中函数的返回类型中所建议的那样。有人能解释一下它是怎么工作的吗?

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

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

还有一点我觉得很重要:您的函数将参数转发给另一个函数。在这种情况下,您应该始终使用右值引用来传递参数。

如果您想做的只是提高可维护性:在RETURNS宏中,有几次尝试试图最大限度地减少返回类型声明和实际返回表达式之间的重复,但我还没有看到任何一次允许函数体包含超过实际返回语句的内容。

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

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

例如

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

声明CCD_ 17是用类型为CCD_ 19的右值调用CCD_。这与您的案例(c)(fn(Arg()))类似,但它不需要任何Arg,因此它适用于没有默认构造函数的类型。

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

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

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

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

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

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

此外,正如gcc错误54111所示,返回类型的推导是非常不可靠的。

(b)的一个变体,不仅使用函数指针,还应该类似

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