Fureeish
Fureeish

Reputation: 13434

Is it possible to make a template variable in lambda signature generic?

Suppose you have a function that takes a std::vector of any type and processes it in some way:

template<typename T>
void foo(std::vector<T> &vec) {
    // work with vec
}

Since C++14, we are able to achieve the same thing with lambdas. In this case, we call them generic lambdas, since we introduce a template-like deduction to them:

auto foo_lambda = [](std::vector<auto> &vec) {
    // work with vec
};

But our options seem quite limited to me. Suppose that I not only have to introduce a type deduction, but I also need to introduce template values. For example, let's change std::vector to std::array:

template<typename T, std::size_t size>
void foo(std::array<T, size> &arr) {
    // work with arr
}

When dealing with template functions, we are able to introduce a template value, which can be used to match argument's needs. Neat.

I wanted to achieve the same functionality with generic lambdas, but I was unable to do so.

Is there a way to introduce a similar, deduced value to a lambda expression so any std::arrays can be used with said lambda, similarily to the second version of the foo() function above?

EDIT: As stated in the comments by Evg, my vector<auto> syntax is non-standard GCC extension. For details see this answer referring to this document.

Upvotes: 5

Views: 714

Answers (3)

max66
max66

Reputation: 66240

Is there a way to introduce a similar, deduced value to a lambda expression so any std::arrays can be used with said lambda, similarily to the second version of the foo() function above?

Yes. But, unfortunately, starting (presumably) from C++20

auto foo_lambda = []<typename T, std::size_t S>(std::array<T, S> & arr)
 { /* ... */ };

In C++14/C++17 you can use decltype() to extract what you need.

In the std::array case, something as

auto foo_lambda = [](auto & arr)
 {
   using T = typename std::remove_reference_t<decltype(arr)>::value_type;
   std::size_t S = arr.size();

   // ...
 };

With other types, you can develop custom type traits to extract the needed elements starting from decltype(arr).

Upvotes: 4

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275750

Your vector<auto> syntax is wrong.

You can tear apart the type of the auto parameter in the return type/body of the lambda using helper functions and trait classes.

\http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0428r1.pdf is a proposal to add []<template T>( std::vector<T>& ){} to the lamguage. Something like it should be in .

I've done double lambdas before:

template<class T>struct tag_t{using type=T;};
template<class T>constexpr tag_t<T> tag{};
template<class Tag>using type_t=typename Tag::type;

auto f = [](auto tag_T){ return []( std::vector<type_t<decltype(tag_T)>> const& v){ /* code */ }; };

and use like:

f(tag<int>)( std::vector<int>{} );

where we use values as template type parameters.

Upvotes: 0

user7860670
user7860670

Reputation: 37599

You can use some dedicated type trait:

#include <type_traits>
#include <utility>
#include <array>

template<typename x_Whatever> struct
is_array: ::std::false_type {};

template<typename x_Item, ::std::size_t x_items_count> struct
is_array<::std::array<x_Item, x_items_count>>: ::std::true_type {};

int main()
{
    auto Do_SomethingWithArray
    {
        [](auto & should_be_array)
        {
            static_assert
            (
                is_array
                <
                    ::std::remove_reference_t<decltype(should_be_array)>
                >::value
            );            
        }
    };
    ::std::array<int, 3> a{};
    Do_SomethingWithArray(a); // Ok
    int x{};
    Do_SomethingWithArray(x); // error
}

online compiler

Upvotes: 5

Related Questions