C++ 推导函子返回类型的一般方法?

C++ 推导函子返回类型的一般方法?,c++,templates,lambda,c++11,C++,Templates,Lambda,C++11,这是一个后续问题 我正在用一种更抽象的方式重新表述它 给定模板函数的伪代码 template <typename Arg, typename Fn> auto ComputeSomething(Arg arg, Fn fn) -> decltype(<decl-expr>) { // do something // ............ return fn(<ret-expr>) } (c) 它假定函子的参数类型为Arg(通常可能不成立),并要求A

这是一个后续问题 我正在用一种更抽象的方式重新表述它

给定模板函数的伪代码

template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(<decl-expr>)
{
// do something
// ............
return fn(<ret-expr>)
}
(c) 它假定函子的参数类型为
Arg
(通常可能不成立),并要求
Arg
是默认可构造的

template <typename Arg, typename Fn>
auto ComputeSomething(Arg arg, Fn fn) -> decltype(fn(Arg())
使用。它向后兼容,消除了代码中所有难看的
declval
痛苦。如果您实际上只是转发值,您仍然需要记住添加右值引用限定符(
&&&

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

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

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

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

template<typename Arg, typename Ret>
Ret ComputeSomething (Arg arg, function<auto (Arg) -> Ret> f)
模板
Ret ComputeSomething(Arg Arg,函数Ret>f)
要使(c)适用于任何东西,您需要2个重载。如(c)所示的第一个,第二个:

模板
Ret ComputeSomething(Arg Arg,std::function fn)

此外,如图所示,返回类型的推断是非常不可靠的

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

e、 g

typedef decltype(fn(std::declval())t;
声明
t
为调用
fn
的结果的类型,其右值类型为
Arg
。这类似于您的案例(c)(
fn(Arg())
),但它不需要任何
Arg
,因此它可以在没有默认构造函数的类型上工作

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


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

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

template <typename Arg, typename Fn>
typename std::result_of<Fn(Arg)>::type ComputeSomething(Arg arg, Fn fn)
模板
typename std::type ComputeSomething的结果(Arg Arg,Fn Fn)

std::declval()
也是需要考虑的,特别是考虑到您提到了一个局部变量。我建议不要使用
std::result\u的
。与基于
decltype
的解决方案不同,这一次不能保证SFINAE可以使用。它也不反映调用的结果类型——它的作用远不止于此,例如与指向成员的指针有关。@LucDanton我一直认为“做得更多”是一种好处。处理指向成员函数的指针的情况非常繁琐,
result\u解决了这个问题。您能更详细地了解SFINAE问题吗?
std::result\u计算函数模板无法处理的结果类型没有意义--
f(a,b,c)
是语法错误,当
f
是指向成员的指针时。至于sFIAE,考虑重载<代码>应用< /COD>,这样<代码>应用(f,ARGS…)<代码>要么导致<代码> f(ARGs…)<代码>,要么,如果格式不好,诉诸<代码> f(*ARGS…)<代码>(为了简单起见,假设这两个表达式从来没有同时形成)。要使调用成功,您需要SFINAE删除其中一个重载。@如果您想处理原始成员函数指针和普通函数,或者只需要将其包装在
mem\u fn
中,LucDanton通常不会对某种
call\u traits
进行分派。我可能必须完成SFINAE部分,因为我不明白。这与函数指针无关(而且
std::mem_fn
无论如何也没有帮助)。关键是,如果您使用
decltype
,那么它就可以工作了。很抱歉这么说,但恐怕这是不可能的,因为后面的返回没有看到函数模板被定义,而正文却被定义了。
template<typename Arg, typename Ret>
Ret ComputeSomething (Arg arg, function<auto (Arg) -> Ret> f)
template <typename Arg, typename Ret>
Ret ComputeSomething(Arg arg, std::function<Ret(Arg)> fn)
typedef decltype(fn(std::declval<Arg>())) t;
template <typename Arg, typename Fn>
typename std::result_of<Fn(Arg)>::type ComputeSomething(Arg arg, Fn fn)