Familiar Template Syntax for Generic Lambdas

Familiar template syntax for generic lambdas

The result of a lambda expression is not a function; it is a function object. That is, it is a class type that has an operator() overload on it. So this:

auto f = []<typename T>( T t ) {};

Is equivalent to this:

struct unnamed
{
template<typename T>
void operator()(T t) {}
};

auto f = unnamed{};

If you want to explicitly provide template arguments to a lambda function, you have to call operator() explicitly: f.operator()<template arguments>(parameters);.

Is (or will be) the use of familiar template syntax in lambda expressions allowed?

The version of the paper that was accepted was N3649, we can see this by going to Evolution Working Group(EWG) Completed Issue 16: N3649, N3560, N3559, N3418 Proposal for Generic (Polymorphic) Lambda Expressions:

Reviewed by EWG in Portland 2012, proceeding with a follow-up paper.

Accepted into the Working Draft in Bristol 2013, as N3649.

Bristol 2013: Do not re-open proposals 2.1 and 2.2 in N3560, they are
considered NAD. The proposals 2.3 and 2.4 are covered by N3649.

Note this references proposal 2.1 and 2.2 as being NAD(Not A Defect) and that they won't be reopened. N3560 was split off from N3418 which was the main proposal and proposal 2.1 in N3560 was:

Allow the use of familiar template syntax in lambda expressions

that paper notes proposal 2.1 was considered controversial:

We admit that supporting the full template parameter list feature
has been deemed controversial (the Portland 2012 straw-poll outcomes
were: 7 SF, 5 F, 3 N, 1 A, 1 SA 1 ) by a few committee members, and
therefore conclude this sub-section with some quotes from a committee
member who was not present in the room during EWG's discussion of
this feature in Portland.

and we can see that N3649 does not contain this proposal my guess from the quote in paper N3560:

"
I think we need more than just
auto. I'm not sure how much more, but I think having just auto would be too limiting
".

was that auto was considered sufficient in the end which would be consistent with saying that the proposal is NAD meaning the issue it attempted to resolve is not really an issue.

C++20 template lambda: how to specify template argument?

foo.template operator()<true>();

is the correct syntax. Try it on godbolt.org

The reason for this strange syntax is because:

  • foo is a generic lambda which implements a template<bool> operator() method.
  • foo.operator()<true>() would interpret < as a comparison operator.

If you want a slightly more readable syntax, try using a std::bool_constant:

auto foo = []<bool arg>(std::bool_constant<arg>) {
...
};

foo(std::bool_constant<true>{});

Try it on godbolt.org

What is the need of template lambda introduced in C++20 when C++14 already has generic lambda?

C++14 generic lambdas are a very cool way to generate a functor with an operator () that looks like this:

template <class T, class U>
auto operator()(T t, U u) const;

But not like this:

template <class T>
auto operator()(T t1, T t2) const; // Same type please

Nor like this:

template <class T, std::size_t N>
auto operator()(std::array<T, N> const &) const; // Only `std::array` please

Nor like this (although this gets a bit tricky to actually use):

template <class T>
auto operator()() const; // No deduction

C++14 lambdas are fine, but C++20 allows us to implement these cases without hassle.

Explicit instantiation of templated lambda

check itself is not a template. It is an object, of an unspecified closure type, that contains

template<auto I> void operator()();

The member function is the template.

The error is due to the attempt at supplying template arguments to check. Which is not a template itself, again. The template parameters of a lambda's function call operator need to be deducible (even if they are named), for the function call syntax to work. That hasn't changed with C++20.

The only way to specify the parameters explicitly is the pretty ugly

check.template operator()<II>()

But instead it may be better to make it deducible.

[&]<auto... II>(std::index_sequence<II...>) {
auto check = [&]<auto I>(std::integral_constant<decltype(I), I>){
};
(check(std::integral_constant<decltype(II), II>{}),...);
}(std::make_index_sequence<N>{});

Specifying size_t explicitly is also an option instead of using decltype(I).

Why are C++20 template lambdas using typename keyword?

The problem is that this already has a meaning:

template <T> void foo();

It's a function template with one template parameter that is a non-type template parameter whose type is T, and that template parameter has no name.

It would be pretty confusing if the same syntax meant very different things depending on if you're introducing a function template or a generic lambda -- which is to say, two very similar contexts serving similar purposes!

Plus then... what would you do if you actually want a non-type template parameter? Just can't have one?

C++ specialize a lambda for a certain type

This type of coding may seem superfluous at first but there are some nice properties we can take advantage of. Regarding the post you mention, I was planning on a part 2, where I'd showcase a neat way of checking whether a type has a specific member (function or data). Say you want to check for a serialize member function; instead of using complicated mechanisms I discovered it gets as easy as :

auto hs = overload( 
[ ](auto&& x) -> decltype(x.serialize(2), std::true_type{}) {
return{}; }, // ^^ this guy ^^
[ ](...) -> std::false_type { return {}; });

demo

The specifics of standardese evolution can be found here, but the reason I posted this is to advocate for such a syntax which, had we have had it, the above could be extended to allow partial ordering among "overloaded" generic lambdas :

auto do_something = overload( 
[ ]<class X>(shared_ptr<X> x) -> decltype(x.serialize(2), std::true_type{}) {
/*do something specific for shared ptrs of X */ return{}; },
[ ]<class X>(X& x) -> decltype(x.serialize(2), std::true_type{}) {
/*do a generic operation for other types that have serialize*/return{}; },
[ ](...) -> std::false_type { /*do nothing*/ return {}; });


Related Topics



Leave a reply



Submit