Functions in C++
lambdas*
Lambda Capture Initializers
This allows creating lambda captures initialized with arbitrary expressions. The name given to the captured value does not need to be related to any variables in the enclosing scopes and introduces a new name inside the lambda body. The initializing expression is evaluated when the lambda is created (not when it is invoked).
int factory(int i) { return i * 10; } auto f = [x = factory(2)] { return x; }; // returns 20 auto generator = [x = 0] () mutable { // this would not compile without 'mutable' as we are modifying x on each call return x++; }; auto a = generator(); // == 0 auto b = generator(); // == 1 auto c = generator(); // == 2
Because it is now possible to move (or forward) values into a lambda that could previously be only captured by copy or reference we can now capture move-only types in a lambda by value. Note that in the below example the p in the capture-list of task2 on the left-hand-side of =
is a new variable private to the lambda body and does not refer to the original p.
auto p = std::make_unique<int>(1); auto task1 = [=] { *p = 5; }; // ERROR: std::unique_ptr cannot be copied // vs. auto task2 = [p = std::move(p)] { *p = 5; }; // OK: p is move-constructed into the closure object // the original p is empty after task2 is created
Using this reference-captures can have different names than the referenced variable.
auto x = 1; auto f = [&r = x, x = x * 10] { ++r; return r + x; }; f(); // sets x to 2 and returns 12
Return type deduction
Using an auto
return type in C++14, the compiler will attempt to deduce the type for you. With lambdas, you can now deduce its return type using auto
, which makes returning a deduced reference or rvalue reference possible.
// Deduce return type as `int`. auto f(int i) { return i; } template <typename T> auto& f(T& t) { return t; } // Returns a reference to a deduced type. auto g = [](auto& x) -> auto& { return f(x); }; int y = 123; int& z = g(y); // reference to `y`
Generic lambda expressions
C++14 allows the auto type-specifier in the parameter list, enabling polymorphic lambdas.
auto identity = [](auto x) { return x; }; int three = identity(3); // == 3 std::string foo = identity("foo"); // == "foo"
Trailing return types
C++11 allows functions and lambdas an alternative syntax for specifying their return types.
int f() { return 123; } // vs. auto f() -> int { return 123; } auto g = []() -> int { return 123; };
This feature is especially useful when certain return types cannot be resolved:
// NOTE: This does not compile! template <typename T, typename U> decltype(a + b) add(T a, U b) { return a + b; } // Trailing return types allows this: template <typename T, typename U> auto add(T a, U b) -> decltype(a + b) { return a + b; }
In C++14, decltype(auto) can be used instead.
Ref-qualified member functions
Member functions can now be qualified depending on whether *this
is an lvalue or rvalue reference.
struct Bar { // ... }; struct Foo { Bar& getBar() & { return bar; } const Bar& getBar() const& { return bar; } Bar&& getBar() && { return std::move(bar); } const Bar&& getBar() const&& { return std::move(bar); } private: Bar bar; }; Foo foo{}; Bar bar = foo.getBar(); // calls `Bar& getBar() &` const Foo foo2{}; Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&` Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&` std::move(foo).getBar(); // calls `Bar&& Foo::getBar() &&` std::move(foo2).getBar(); // calls `const Bar&& Foo::getBar() const&`