134

How does generic lambda work (auto keyword as an argument type) in C++14 standard?

Is it based on C++ templates where for each different argument type compiler generates a new function with the same body but replaced types (compile-time polymorphism) or is it more similar to Java's generics (type erasure)?

Code example:

auto glambda = [](auto a) { return a; };
sasha.sochka
  • 14,395
  • 10
  • 44
  • 68

4 Answers4

158

Generic lambdas were introduced in C++14.

Simply, the closure type defined by the lambda expression will have a templated call operator rather than the regular, non-template call operator of C++11's lambdas (of course, when auto appears at least once in the parameter list).

So your example:

auto glambda = [] (auto a) { return a; };

Will make glambda an instance of this type:

class /* unnamed */
{
public:
    template<typename T>
    T operator () (T a) const { return a; }
};

Paragraph 5.1.2/5 of the C++14 Standard Draft n3690 specifies how the call operator of the closure type of a given lambda expression is defined:

The closure type for a non-generic lambda-expression has a public inline function call operator (13.5.4) whose parameters and return type are described by the lambda-expression’s parameter-declaration-clause and trailing-return-type respectively. For a generic lambda, the closure type has a public inline function call operator member template (14.5.2) whose template-parameter-list consists of one invented type template-parameter for each occurrence of auto in the lambda’s parameter-declaration-clause, in order of appearance. The invented type template-parameter is a parameter pack if the corresponding parameter-declaration declares a function parameter pack (8.3.5). The return type and function parameters of the function call operator template are derived from the lambda-expression’s trailing-return-type and parameter-declarationclause by replacing each occurrence of auto in the decl-specifiers of the parameter-declaration-clause with the name of the corresponding invented template-parameter.

Finally:

Is it similar to templates where for each different argument type compiler generates functions with the same body but changed types or is it more similar to Java's generics?

As the above paragraph explains, generic lambdas are just syntactic sugar for unique, unnamed functors with a templated call operator. That should answer your question :)

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 10
    However, they also allow a locally defined class with a template method. Which is new. – Yakk - Adam Nevraumont Jun 21 '13 at 12:14
  • @Yakk: True. In fact, I was wondering whether that restriction could not be lifted in general (i.e. not just for closure types), and asked about it [here](https://groups.google.com/a/isocpp.org/forum/#!msg/std-discussion/Xck6f50kOjU/Ai1Qnq-zs7gJ). – Andy Prowl Jun 21 '13 at 12:19
  • nice answer of course, but most of all: congratulations on the Legendary badge! – TemplateRex Jun 21 '13 at 12:59
  • The bold passage seems like an abstraction leak - though it makes perfect sense, leave it up to the implementation IMO. – Lightness Races in Orbit Jul 16 '13 at 14:22
  • 2
    @Yakk: Wasn't the restriction for function-local templates dropped altogether with C++11 already? – Sebastian Mach Jul 17 '13 at 07:13
  • 2
    @phresnel: Nope, that restriction has not been lifted – Andy Prowl Jul 17 '13 at 09:04
  • 2
    @AndyProwl: I realise my mistake. What was lifted indeed was using local types as template arguments (as in `int main () { struct X {}; std::vector x; }`) – Sebastian Mach Jul 17 '13 at 10:22
  • 1
    @phresnel: Right, that has changed indeed – Andy Prowl Jul 17 '13 at 13:48
  • 1
    @LightnessRacesinOrbit You might be able to do some metaprogramming on `template operator()` with that kind of specification that would catch `auto` lambdas... but not sure how, because function and method `template`s are strange beasts which can be named but not indirectly referred to. Hmm. – Yakk - Adam Nevraumont Jul 17 '13 at 14:03
  • 1
    @LightnessRacesinOrbit: C++ is a biiiig abstraction leak already. That's why it is so fun. That is also why many people hate it. – v.oddou Dec 09 '14 at 03:34
30

Unfortunately, they are not part of C++11 (http://ideone.com/NsqYuq):

auto glambda = [](auto a) { return a; };

int main() {}

With g++ 4.7:

prog.cpp:1:24: error: parameter declared ‘auto’
...

However, the way it might be implemented in C++14 as per the Portland proposal for generic lambdas:

[](const& x, & y){ return x + y; }

This would yield for the biggest part the usual creation of an anonymous functor class, but with the lack of types the compiler would emit a templated member-operator():

struct anonymous
{
    template <typename T, typename U>
    auto operator()(T const& x, U& y) const -> decltype(x+y)
    { return x + y; }
};

Or as per the newer proposal Proposal for Generic (Polymorphic) Lambda Expressions

auto L = [](const auto& x, auto& y){ return x + y; };

--->

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;

So yes, for every permutation of parameters, a new instantiation would arise, however, the members of that functor would still be shared (i.e. the captured arguments).

Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
  • 6
    That proposal to allow dropping the _type-specifier_ is utterly grotesque. – Lightness Races in Orbit Jul 16 '13 at 14:23
  • They went in with [g++-4.9](https://gcc.gnu.org/gcc-4.9/changes.html). You need to supply `-std=c++1y`. – emsr May 13 '14 at 13:21
  • I didn't realize ideone doesn't have gcc-4.9 and C++14 yet. – emsr May 13 '14 at 16:42
  • question : does this `auto` has the same deduction rules than the classic auto ? If we refer to the templated analogy, it would mean the auto is not auto, it is the same rules as template type deduction. Then the question is : is template deduction equivalent to `auto` ? – v.oddou Dec 09 '14 at 03:37
  • @v.oddou: "Classic auto" is good. To me, "classic auto" means "Stack Variable", and was once used in contrast to `static` or `register` :) Anyways, yes, using `auto` there means that under the hood, a normal template is generated. In fact, a lambda will be replaced compiler-internally by a functor class, and a `auto` parameter means that `template ... (T ...)` will be emitted. – Sebastian Mach Dec 09 '14 at 09:26
21

It's a proposed C++14 feature (not in C++11) similar (or even equivalent) to templates. For instance, N3559 provides this example:

For example, this generic lambda-expression containing statement:

auto L = [](const auto& x, auto& y){ return x + y; };

might result in the creation of a closure type, and object that behaves similar to the struct below:

struct /* anonymous */
{
    template <typename T, typename U>
    auto operator()(const T& x, U& y) const // N3386 Return type deduction
    { return x + y; }
} L;
Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
1

From the book, C++ Templates : The Complete Guide (2nd Edition), section 5.5.2.

5.5.2 Generic Lambdas and Member Templates

Note that generic lambdas, introduced with C++14, are shortcuts for member templates. A simple lambda computing the “sum” of two arguments of arbitrary types:

[] (auto x, auto y) {
  return x + y;
}

is a shortcut for a default-constructed object of the following class:

class SomeCompilerSpecificName {
  public:
    SomeCompilerSpecificName();  // constructor only callable by compiler
    template<typename T1, typename T2>
    auto operator() (T1 x, T2 y) const {
      return x + y;
    }
};

This means that for the generic lambda the compiler is generating a templated class. It follows that for auto the type deduction rules for templates will apply.

An interesting corollary is that you can inherit from lambdas.

More on inheriting from lambdas:

  1. C++ Weekly - Ep 40 - Inheriting From Lambdas
  2. What does it mean to inherit from lambda?
  3. Code sample
Sahil Singh
  • 3,352
  • 39
  • 62