在没有实例化 lambda 变量的情况下推导捕获 lambda 函数 `operator()` 的返回类型

问题描述 投票:0回答:2

我想做的是从执行 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';
}
c++ c++23
2个回答
0
投票

你是对的,因为一切都在

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_ ) }
  {}

演示


0
投票

我这里有一堆评论:


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
会有用。

© www.soinside.com 2019 - 2024. All rights reserved.