【问题标题】:How to call a c++ templated lambda within a lambda?如何在 lambda 中调用 c++ 模板化 lambda?
【发布时间】:2021-06-07 22:48:02
【问题描述】:

我想知道是否可以将 lambda 的整数参数作为其模板参数传递给嵌套模板 lambda。如果可能,那么使用模板 lambda 会带来很多好处。 例如,使用模板 lambda 是一件尴尬的工作。考虑

auto foo=[]<int N>(){
    return N;
};
std::cout<<foo.operator()<5>();

在这里,当我们调用 lambda foo 时,我们必须使用模板参数 &lt;5&gt; 调用其原生 operator(),然后由另一个 () 调用它。 所以,如果我们可以设计一个 lambda 来传递一个普通的整数参数,并且 lambda 在内部调用它的本地模板 lambda 并完成工作,那么它就可以省去所有这些尴尬。我设计了这个简单的 lambda 来将参数 N 转换为其本机模板 lambda 的参数。但是我被困在如何调用这个模板 lambda 上。

auto foo=[](int N){
    auto f=[]<int N>(){
        return N;
    };
    return f.operator()<N>(); //this cannot compile!
};

我的问题是如何在foo 中调用这个f?或者这个想法完全不可能,因为模板参数要求N 是 constexpr 或 compile-time-constant?

感谢cigien闪电般的快速回复!好像

这个想法完全不可能,因为模板参数要求 N 是 constexpr 或 compile-time-constant

除了Chris 建议的可能解决方法之外,我尝试使用std::integral_constant 来解决我原来的template-lambda-invoking-awkwardness。这是我能走多远:

#define  constInt(N) integral_constant<int, N>{}

auto foo=[]<int N>(std::integral_constant<int, N>&& n){
    auto f=[](){
        return array<int, N>{};
    };
    return f();
};

auto ary1=foo(integral_constant<int, 5>{});
auto ary2=foo(constInt(5));
static_assert(is_same_v<decltype(ary1), array<int, 5>>);
static_assert(is_same_v<decltype(ary2), array<int, 5>>);

通过使用宏constInt,它节省了一点打字工作。那么我的问题是: 这是我们可以通过模板 lambda 调用实现的最佳效果吗?如果模板参数是我们无法利用std::integral_constant 的类型怎么办?

根据Davis 使用标签的建议,我将我的问题重新表述为以下具体任务: **创建一个 lambda 以返回一个 std::array&lt;T,N&gt;

这是通常的 lambda:

auto createArray=[]<typename T, size_t N>(){
    return array<T, N>{};
};

但是用模板参数调用它是一种尴尬的方式:

auto ary4=createArray.operator()<string, 5>();
static_assert(is_same_v<decltype(ary4), array<string, 5>>);

我们可以通过使用std::itegral_constant 之类的普通参数来避免使用这个.operator()&lt;string, 5&gt; 吗?例如,createArray(class_tag&lt;string&gt;{},integral_constant&lt;size_t, 5&gt;{}); 那我们应该如何实现这个class_tag呢?

事实证明这是一项非常简单的任务,这是我的实现演示:

template<typename T>
 struct class_tag{};
 auto createArray2=[]<typename T, size_t N>(class_tag<T>&&,   integral_constant<size_t, N>&&){
    return array<T, N>{};
};
auto ary5=createArray2(class_tag<string>{}, integral_constant<size_t, 5>{});
static_assert(is_same_v<decltype(ary5), array<string, 5>>);

所以,我想它在下面所有伟大的 cmets 的帮助下解决了我的问题。谢谢大家!

【问题讨论】:

  • 这个想法完全不可能,因为模板参数要求 N 是 constexpr 或 compile-time-constant?”。目前,是的。
  • 如果是这样,是否有任何解决方法可以解决问题?
  • 您可以将N 当作std::integral_constant。我不知道这是否是您正在寻找的那种技巧。
  • 任何可以作为模板参数的东西(甚至是模板)都可以包装在标签类模板中(如果不是std::integral_constant)。
  • 任何可以作为模板参数的东西(甚至是模板)都可以包装在标签类模板中(如果不是 std::integral_constant)。你能举个例子吗这样做?例如,我想创建一个简单的 lambda,只返回 array。能避免模板 lambda 调用的尴尬吗?

标签: c++ templates lambda


【解决方案1】:
template<auto x>
using constant_t = std::integral_constant<std::decay_t<decltype(x)>, x>;
template<auto x>
constexpr constant_t<x> constant{};
template<class T>
struct tag_t { using type=T; };
template<class T>
constexpr tag_t<T> tag{};


auto make_array=[]<auto N, class T>(constant_t<N>, tag_t<T>){
  return std::array<T, N>{};
};
std::cout << make_array(constant<3>, tag<int>).size() << "\n";

Live example.

这里我使用了标签调度技术。标签在其类型中携带有效负载,因此可以按值传递,然后可以将“有效负载”提取为编译时常量。

如果需要,您甚至可以将其设为两阶段 lambda,其中将模板参数传递给外部 lambda,然后由内部 lambda 使用常量和类型:

auto make_array=[]<auto N, class T>(constant_t<N>, tag_t<T>) {
  return [](auto&&...ts)requires (sizeof...(ts)==N && (std::is_convertible<decltype(ts), T>&&...))
  {
    return std::array<T,N>{ decltype(ts)(ts)... );
  };
};

使用如下:

make_array(constant<3>, tag<int>)(1,2,3);

在旧版本的 C++ 中,您可以改用 auto 参数:

auto make_array=[](auto Size, auto Type) {
  using T = typename decltype(Type)::type;
  constexpr auto N = decltype(Size)::value;

  return [](auto&&...ts)
  {
    return std::array<T,N>{ decltype(ts)(ts)... );
  };
};

constant&lt;7&gt; 必须替换为

template<std::size_t N>
using ksize_t = std::integral_constant<std::size_t, N>;
template<std::size_t N>
constexpr ksize_t<N> ksize{};

因为缺少auto非类型模板参数。

【讨论】:

    【解决方案2】:

    如果您将模板移到 lambda 之外,它会非常方便地工作:

    template<int N>
    auto lambda = []{ return N; };
    
    int main()
    {
        return lambda<5>();
    }
    

    https://gcc.godbolt.org/z/PsvYvW36Y

    【讨论】:

      【解决方案3】:

      所以,我其实有答案:lambda 参数不能直接翻译成模板参数。需要一些辅助类的技巧,例如integral_constant、integer_sequence、class_tag 等。由于函数参数不被视为编译时常量,因此无法直接将其用作模板参数。但是,为了解决我避免使用 lambda 模板参数调用的直接要求,使用这些帮助程序类是可行的。 (参见上面的 cmets 和示例代码。)

      【讨论】:

      • 参见上面的 cmets 和示例代码。”不是一个好主意,因为 cmets 可能随时被删除。您所指的 which cmets 和 which 代码也不一定清楚。请将该相关信息添加到您的答案中以使其独立。当然,您仍然可以归因于 cmets。
      猜你喜欢
      • 1970-01-01
      • 2019-11-06
      • 1970-01-01
      • 1970-01-01
      • 2018-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多