Declaring a function using auto, decltype, = , [ ] - c++

I found a function defined this way on:
http://en.cppreference.com/w/cpp/language/decltype
I have never seen this syntax used to defined a function, can anyone explain this?
This only seems to work with auto and decltype
#include <iostream>
using namespace std;
auto f = [](int a, int b)->int
{
return a * b;
};
/*
int f = [](int a, int b) //DOES NOT WORK
{
return a * b;
};
*/
int main()
{
int a = 2, b = 3;
cout<<f(a,b);
return 0;
}
I'm not certain if the following function uses dectype when we do:
->int
If it does, then how?

auto f = [](int a, int b)->int
{
return a * b;
};
The f here is a global variable of some anonymous type, holding an instance of the anonymous function object defined after the operator=. Such anonymous function objects are called lambdas, they can occur everywhere where you can have some expression:
int main(int, char**) {
([](int a) {
cout << "lambda called with " << a << endl;
})(42);
return ([](int a, int b) { return a - 2 * b; })(42, 21);
}
The general syntax of such a lambda expression is the following:
"["CAPTURE"]" PARAMETERS ["mutable"] [RETURN] { BODY }
CAPTURE being a list of zero ore more of
variable from the enclosing scope, capture by value
variable from the enclosing scope, preceeded by &, capture by reference
& means capture all variables from the enclosing scope by reference
= means capture all variables from the enclosing scope by value
PARAMETERS are the usual parameter lists you know from functions, optionally (since C++14) with auto and type deduction.
mutable allows the lambda to mutate its captured variables.
The optional RETURN contains a specification of the return type, e.g. -> void and the BODY contains arbitrary expressions and statements.
Note that this is only a rough sketch of the syntax, but it should get you started. You can find out more about lambdas in the standard, searching for "C++11 lambda" on Google or for example here.
Btw, a lambda is nothing spooky, you can think of your f as being a rough equivalent of the following, "old style" C++ code:
struct {
int operator()(int a, int b) const {
return a * b;
}
} f;

When you use:
auto f = [](int a, int b)->int
{
return a * b;
};
the type of f is type of the lambda expression. It is not int, the return type of the lambda expression.
As far as the -> int part goes, that's the only syntax available to explicitly specify the return type of a lambda expression. It that is omitted, the return type is deduced by the compiler using an algorithm specified by the standard:
5.1.2 Lambda expressions
4 If a lambda-expression does not include a lambda-declarator, it is as if the lambda-declarator were (). If a lambda-expression does not include a trailing-return-type, it is as if the trailing-return-type denotes the
following type:
— if the compound-statement is of the form
{ attribute-specifier-seqopt return expression ; }
the type of the returned expression after lvalue-to-rvalue conversion (4.1), array-to-pointer conversion(4.2), and function-to-pointer conversion (4.3);
— otherwise, void.
[ Example:
auto x1 = [](int i){ return i; }; // OK: return type is int
auto x2 = []{ return { 1, 2 }; }; // error: the return type is void (a
// braced-init-list is not an expression)
—end example ]

Related

Why does C++17 if statement with initializer not work as expected?

struct A
{
auto g1()
{
return true;
}
void f()
{
if (auto b = g1(); b) // ok
{
return;
}
if (auto b = g2(); b) // error: use of 'auto A::g2()' before deduction of 'auto'
{
return;
}
}
auto g2()
{
return true;
}
};
Why does C++17 if statement with initializer not work as expected?
Because the standard says so (quote from latest draft):
[dcl.spec.auto.general]
If a variable or function with an undeduced placeholder type is named
by an expression ([basic.def.odr]), the program is ill-formed. Once a
non-discarded return statement has been seen in a function, however,
the return type deduced from that statement can be used in the rest of
the function, including in other return statements.
[Example 4:
auto n = n; // error: n's initializer refers to n
auto f();
void g() { &f; } // error: f's return type is unknown
To add a little bit of clarification, the "declaration" of g2 is "seen" because the definition of g1 is in the complete-class context. But that does not extend to having seen the definition of g2.

Lambda: uncaptured objects in unevaluated context

I'd appreciate a pointer to the Standard paragraph stating that the following code is well-formed:
int main() {
int var = 0;
return [] { return sizeof(var); }(); // var is not captured!
}
Similar example appears e.g. in § 8.4.5.2, but I could not find any verbal description of it.
It's specified in terms of when an entity must be captured, rather than in terms of when it may not.
[expr.prim.lambda.capture] (with some omissions)
8 ... If a lambda-expression or an instantiation of the function
call operator template of a generic lambda odr-uses this or a variable
with automatic storage duration from its reaching scope, that entity
shall be captured by the lambda-expression. ...
[ Example:
void f1(int i) {
int const N = 20;
auto m1 = [=]{
int const M = 30;
auto m2 = [i]{
int x[N][M]; // OK: N and M are not odr-used
// ...
};
};
// ...
}
— end example ]
The key in your code sample is that var is not odr-used, because it is an unevaluated operand. As such, it does not need to be captured.

lambda fct returning reference

I have tried hard to make a lambda function return a value by reference without making a copy of the referenced value.
My code example below illustrates the problem. It compiles and runs ok, but with the "//" commented line instead of the line above, it doesn't.
I have found two workarounds (both illustrated in my example):
wrap the result with std::ref()
return a pointer instead of a reference
But both workarounds are not what I really want, and I do not understand why they are necessary: The expression "makeRefA()" has already the type the lambda function returns (const A&) and thus must neither be copied nor converted. By the way: The copy constructor is really called when I do not explicitly delete it (which in my "real" code is a performance problem).
To me it looks like a compiler bug, but I have tried with several C++11-compilers which all show up the same error. So is there something special concerning the "return" statement in a lambda function?
#include <functional>
#include <iostream>
struct A {
A(int i) : m(i) { }
A(const A&) = delete;
int m;
};
void foo(const A & a) {
std::cout << a.m <<'\n';
}
const A & makeRefA() {
static A a(3);
return a;
}
int main() {
std::function<const A&()> fctRef = [&]
{ return std::ref(makeRefA()); }; //compiles ok
//{ return makeRefA(); }; //error: use of deleted function 'A::A(const A&)'
foo(fctRef());
std::function<const A*()> fctPtr =
[&] { return &makeRefA(); };
foo(*fctPtr());
return 0;
}
output:
3
3
By default, the automatically-deduced type of a lambda is the non-reference version of a type
... the return type is the type of the returned expression (after lvalue-to-rvalue, array-to-pointer, or function-to-pointer implicit conversion); (source)
If you want a return type with a reference, you will have to specify it more explictly. Here are some options:
[&]()
-> decltype( makeRefA() )
{ return makeRefA()); };
or simply be fully explicit about the return type with ->
[&]()
-> const A&
{ return makeRefA(); }
If using C++14, then simply use decltype(auto),
[&]()
-> decltype(auto)
{ return makeRefA(); }
The rules for decltype can be complicated at times. But the fact that makeRefA() is an expression (as opposed to simply naming a variable) means that the type of the expression (const A&) is faithfully returned by decltype( makeRefA() ).
You can specify the return type
#include <functional>
#include <iostream>
struct A {
A(int i) : m(i) { }
A(const A&) = delete;
int m;
};
void foo(const A & a) {
std::cout << a.m <<'\n';
}
const A & makeRefA() {
static A a(3);
return a;
}
int main() {
std::function<const A&()> fctRef = [&]()->const A&
// { return std::ref(makeRefA()); }; //compiles ok
{ return makeRefA(); }; // works
foo(fctRef());
std::function<const A*()> fctPtr =
[&] { return &makeRefA(); };
foo(*fctPtr());
return 0;
}
According to http://en.cppreference.com/w/cpp/language/lambda, these rules apply to lambdas with no trailing return type:
In C++11, lvalue-to-rvalue, array-to-pointer, or function-to-pointer implicit conversion are applied to the type of the returned expression. (Here, the lvalue-to-rvalue conversion is what's hitting you.)
In C++14 and later, the type is deduced as for a function whose return type is declared auto; and that in turn follows the rules for template argument deduction. Then, since auto includes no reference specification, that means that references and cv-qualifiers will be ignored.
The effect is probably desirable in most situations: for example, in this lambda expression
[](const std::vector<int>& v) { return v[0]; }
you probably intend to return an int, even though std::vector<int>::operator[] const returns const int&.
As others have mentioned, you can override this behavior by giving an explicit trailing return type.

Generic lambda with std::function does not capture variables

I'm trying to use the generic lambda of C++14, but got a trouble with std::function.
#include <iostream>
#include <functional>
int main()
{
const int a = 2;
std::function<void(int)> f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
This fails to compile with an error message saying that error: ‘a’ was not declared in this scope.
It works if I change it to (int b).
Is it a bug? or am I missing something?
The version of GCC i'm using is 4.9.2.
I can reproduce this unless I do any of the following:
remove const from a
name a in the capture-list
change std::function<void(int)> to auto
make the lambda non-generic by changing auto b to int b
use Clang (e.g. v3.5.0)
I believe that this is a compiler bug related to optimisations and a failure to detect odr-use in a generic lambda (though it's interesting that setting -O0 has no effect). It could be related to bug 61814 but I don't think it's quite the same thing, therefore:
I have raised it as GCC bug 64791.
(Update: this bug has since been marked as fixed in GCC 5.0.)
Certainly I can't find anything obvious in the C++14 wording that should disallow your code, though there's very little "obvious" in general in the new C++14 wording. :(
[C++14: 5.1.2/6]: [..] For a generic lambda with no lambda-capture, the closure type has a public non-virtual non-explicit const conversion function template to pointer to function. The conversion function template has the same invented template-parameter-list, and the pointer to function has the same parameter types, as the function call operator template. [..]
[C++14: 5.1.2/12]: A lambda-expression with an associated capture-default that does not explicitly capture this or a variable with automatic storage duration (this excludes any id-expression that has been found to refer to an init-capture's associated non-static data member), is said to implicitly capture the entity (i.e., this or a variable) if the compound-statement:
odr-uses (3.2) the entity, or
names the entity in a potentially-evaluated expression (3.2) where the enclosing full-expression depends on a generic lambda parameter declared within the reaching scope of the lambda-expression.
[ Example:
void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g = [](auto a) {
f(x); // OK: calls #1, does not capture x
};
auto g2 = [=](auto a) {
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK: is a dependent expression, so captures x
};
}
—end example ] All such implicitly captured entities shall be declared within the reaching scope of the lambda expression. [ Note: The implicit capture of an entity by a nested lambda-expression can cause its implicit capture by the containing lambda-expression (see below). Implicit odr-uses of this can result in implicit capture. —end note ]
[C++14: 5.1.2/13]: An entity is captured if it is captured explicitly or implicitly. An entity captured by a lambda-expression is odr-used (3.2) in the scope containing the lambda-expression. [..]
int main() {
const int a = 2;
auto f = [&](auto b) { std::cout << a << ", " << b << std::endl; };
f(3);
}
Don't know if it should work with std::function but this works for sure.
Further investigation:
I created a class to mimic as closely as possible the lambda:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
auto operator()(int b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{};
f2(3); // <- this works
This suggests that your example should have worked. After all lambdas are the same in behavior with an object who has the operator() overloaded and fields for the captured variables.
If we change the class to get to the auto part:
This doesn't work:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
auto operator()(auto b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{}; // <-- doesn't work
However this works:
class Functor {
private:
int const x;
public:
Functor() : x{24} {}
template <class T>
auto operator()(T b) const -> void { cout << x << " " << b << endl; }
};
std::function<auto(int)->void> f2 = Functor{}; // <-- this works
So most likely it is related to the use of auto as parameter of lambda/functions, a feature new to C++14 so most likely without a mature implementation.

C++11 function definitions with auto keyword

I am curious about using auto keyword in C++11.
for function definition, you must write the return type of function:
auto f (int x) -> int { return x + 3; }; //success
auto f (int x) { return x + 3; }; //fail
but in this example the both of them will work:
auto f = [](int x) { return x + 3; }; //expect a failure but it works
auto f = [](int x) -> int { return x + 3; }; // this is expected code
thanks.
In C++11, a lambda expression can omit its return type if it can deduce the exact one without ambiguity. However, this rule doesn't apply to the regular function.
int f() { return 0; } // a legal C++ function
auto f() -> int { return 0; } // a legal C++ function only in C++11
auto f() { return 0; } // an illegal C++ function even if in C++11
If you are required to specify the return type after "->" (as it is in C+11) - what is the point to have "auto" in a function declaration? This is as if to say: "This function can return anything, but actually it is only this type". Uh?
Then if you are not required to specify the return type after "->" (as it is in C++14): imagine you don't have the source code of the function, but object file and the header. How do you know what the function returns (the return type)?
So far it seems that "auto" in a function declaration is yet another way to write non-readable code. As if in C++ there are not enough ways to do it already.
While inside function body "auto" is a nice "syntax sugar".
Or am I missing something?