我想做的是从执行 lambda 的类内部推断出捕获 lambda 的返回类型(从不存储),这样返回类型就不需要成为类签名的一部分。
由于 lambda 捕获默认构造函数被删除所以我写了这样的代码:
using AppliedReturnType =
decltype
(
std::apply
(
[]( auto& ...items )
{
std::optional< ApplyLambda > lambda; // optional used here to work around construction
return lambda.value()( items... ); // value used without initialization
},
t_
)
);
我的假设是否正确,因为我只在 decltype 评估中使用此代码,所以我会说这样的代码不是问题,无论运行时的 std::optional 实现/UB 是什么?
出于好奇,有没有更好的方法来做这样的事情? (必须没有类模板列表中的类型)
下面是一个简化的实现:
#include <iostream>
#include <optional>
#include <tuple>
template< typename ApplyLambda, typename Tuple >
class C
{
public:
C( ApplyLambda& lambda, Tuple&& t )
: t_{ std::move( t ) }
, result_
{
std::apply
(
[&lambda]( auto& ...items )
{
return lambda( items... );
},
t_
)
}
{}
auto result() const { return result_; }
private:
Tuple t_;
using AppliedReturnType =
decltype
(
std::apply
(
[]( auto& ...items )
{
std::optional< ApplyLambda > lambda;
return lambda.value()( items... );
},
t_
)
);
AppliedReturnType result_;
};
int main()
{
int i = 10;
auto lambda = [&i]( auto&... items ) { return ( items + ...) + i; };
C c{ lambda, std::tuple{ 1, 2, 3 } };
std::cout << c.result() << '\n';
}
你是对的,因为一切都在
decltype
中,所以没有问题,尽管你可以用简化这些东西
using AppliedReturnType = decltype(std::apply(std::declval<ApplyLambda>(), t_));
正如评论中指出的那样,您的构造函数中不需要内部 lambda
C( ApplyLambda& lambda, Tuple&& t )
: t_{ std::move( t ) }
, result_{ std::apply( lambda, t_ ) }
{}
我这里有一堆评论:
template< typename ApplyLambda, typename Tuple >
不要命名模板参数
Lambda
- 没有理由永远require lambda,任何可调用的都可以。所以只是F
,或ApplyF
,或Callable
,或什么的。
同样的事情也适用于变量的名称。
这个:
[&lambda]( auto& ...items )
{
return lambda( items... );
},
这是一个非常长且不正确的写作方式
lambda
。它在这里工作是因为你将元组作为左值传递,但如果你转发它,那么元组的元素将被移动,并且auto&...
不会编译。
只是:
lambda
.
像这样:
using AppliedReturnType =
decltype
(
std::apply
(
[]( auto& ...items )
{
std::optional< ApplyLambda > lambda;
return lambda.value()( items... );
},
t_
)
);
如上,这里传入的 lambda 可能只是
lambda
,但您还没有那种类型的对象 - 所以您可以使用 declval
:
using AppliedReturnType = decltype(std::apply(std::declval<F&>(), std::declval<Tuple&>());
注意
&
,因为你将它作为左值传递。
这里:
C( ApplyLambda& lambda, Tuple&& t )
不要通过左值引用获取可调用对象——因为它(具有讽刺意味的是)禁止传入 lambdas。
把它们放在一起:
template <typename F, typename Tuple>
class C
{
using R = decltype(std::apply(std::declval<F&>(), std::declval<Tuple&>()));
public:
Tuple t_;
R result_;
public:
C( F f, Tuple&& t )
: t_{ std::move( t ) }
, result_{std::apply(f, t_)}
{}
R result() const { return result_; }
};
最后一点,鉴于
C
存在,我不确定为什么 std::apply
会有用。