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)