简体   繁体   中英

How to call a c++ templated lambda within a lambda?

I am wondering if it is possible to pass a integer parameter of a lambda to a nested template lambda as its template argument. If it is possible then it gives a lot of advantages to use template lambda. For example, using a template lambda is an awkward work. Considering

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

Here when we are calling lambda foo , we have to invoke its native operator() with template argument <5> and then call it by another () . So, if we can design a lambda to pass a normal integer parameter and internally the lambda call its local template lambda and do the work, it then can save all this awkwardness. I designed this simple lambda to convert parameter N to its native template lambda's argument. But I am stuck on how to call this template lambda.

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

My question is how I can call this f within foo ? Or is this idea entirely impossible because template argument requires N to be constexpr or compile-time-constant?

Thank you for cigien 's lightening fast response! It seems

this idea entirely impossible because template argument requires N to be constexpr or compile-time-constant

And further to Chris suggestion of a possible workaround, I try to solve my original template-lambda-invoking-awkwardness by using an std::integral_constant . This is how far I can get:

#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>>);

By using a macro constInt , it saves a little typing work. Then my question is: Is this the best we can achieve with template lambda invoking? What if the template parameter is a type that we cannot take advantage of std::integral_constant ?

With Davis 's suggestion of using a tag, I am rephrasing my question as this concrete task below: **creating a lambda to return a std::array<T,N>

Here is usual lambda:

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

But calling it is an awkward way with template parameter:

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

Can we avoid using this .operator()<string, 5> by just using normal parameter like std::itegral_constant ? For example, something like createArray(class_tag<string>{},integral_constant<size_t, 5>{}); Then how should we implement this class_tag ?

It turns out this is a very easy task and here is my implementation demo:

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>>);

So, I guess it solves my issue with help of all great comments below. Thank you for all!

If you move template outside of lambda it will work very conveniently:

template<int N>
auto lambda = []{ return N; };

int main()
{
    return lambda<5>();
}

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

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 .

Here I use a tag dispatching technique. The tags carry the payload within their type, so can be passed by value and then the "payload" can be extracts as compile-time constants.

You can even make it a two-stage lambda if you want, where the template arguments are passed to an outer lambda, then the constants and types are used by an inner 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)... );
  };
};

which is used like:

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

In older versions of C++, you can use auto arguments instead:

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)... );
  };
};

But constant<7> has to be replaced with

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{};

because of the lack of auto non-type template parameters.

So, I actually have the answer: lambda parameter cannot be directly translated into template argument. Some trick of helper class is needed such as integral_constant, integer_sequence, class_tag etc. And because function parameter is not considered as compile-time constant, there is no way of directly using it as template argument. However, to solve my immediate requirement to avoid using lambda template argument call, it is workable by using these helper class. (see above comments and example code.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM