What I'm trying to do is eat exceptions when constructing an object that may be invalid. It'd be perfect for use of std::optional, but I don't believe the omission of std::optional changes the error I see: the object is being captured and used before it's been initialized. I don't believe it should be captured in the first place because we haven't reached a sequence point to the best of my knowledge (does a lambda initialization count as a sequence point?). Moreover, the bug is IMO easily catchable human error (and even does get caught... depending upon circumstances).
How (more importantly, why) is the lambda able to capture and use the not-yet-initialized foo?
https://godbolt.org/g/IwcHrV
#include <string>
using namespace std;
void foo() {
string foo = [&]()->string{
// using foo before it's been initialized == undefined behavior
auto guessed_foo = to_string(1234);
if ( begin(foo) == end(foo) ) {
return guessed_foo;
}
return {};
}();
}
Compiler exited with result code 0
But... replacing the declaration of string foo with auto foo does appear to cause an error similar to what I'd like to see.
https://godbolt.org/g/GfE4WH
#include <string>
using namespace std;
void foo() {
auto foo = [&]()->string{
auto guessed_foo = to_string(1234);
if ( begin(foo) == end(foo) ) {
return guessed_foo;
}
return {};
}();
}
error: variable 'foo' declared with 'auto' type cannot appear in its own initializer
Note that I found this using GCC 6.2 on Ubuntu 16.04 LTS. The configuration in Godbolt is using clang 3.9.1. Both are configured for c++14.
So my questions are:
Why is the lambda capture for an initialization of a non-auto-declared variable able to capture and use the (not-yet-initialized) variable?
Why does auto get (in my opinion, correctly) caught and errored?
Moreover, why the difference between the above two? It sounds like compiler bugs, but... is there something specific in the standard declaring this to be correct behavior?
This could be taken as an argument for auto keyword?
The second snippet runs into [dcl.spec.auto]/10:
If the type of an entity with an undeduced placeholder type is needed
to determine the type of an expression, the program is ill-formed.
The type of foo is needed to determine the type of the expression foo within the lambda body, but at that point you haven't deduced foo's type yet, so the program is ill-formed.
As to why you are allowed to capture something before its initialization, see generally Why is 'int i = i;' legal?. We have many examples of recursive lambdas using std::function:
std::function<void(int)> foo = [&foo](int i){ return foo(i - 1); };
Related
For example:
void foo()
{
if constexpr (...)
int x = 5;
else
double x = 10.0;
bar(x); // calls different overloads of bar with different values
}
It's common case in D lang, but I didn't found info about C++17.
Of course, it is possible to use something like
std::conditional<..., int, double>::type x;
but only in elementary cases. Even different initializators (as above) creates big problem.
There is no way this code could work. The problem is that x is out of scope when you are calling bar. But there is a workaround:
constexpr auto t = []() -> auto {
if constexpr(/* condition */) return 1;
else return 2.9;
}();
bar(t);
To explain a little bit, it uses instantly invoked lambda expression along with auto return type deduction. Therefore we are giving t value in place and it does not get out of the scope.
Of course, it wouldn't work if the if statement couldn't have been evaluated at compile time. And if you want to do some runtime operations inside of this lambda you cannot have t as constexpr, but it will still work.
There are two ways this won't work.
First, a variable is limited to the scope in which it's declared. Leaving out the braces isn't gonna fool the compiler: int x = 5 is still in its own scope, and disappears immediately after it appears.
Second, the relaxed grammar rules for if constexpr only apply within the if constexpr's body. It would be infeasible to allow the context created in the body to leak out to the surrounding scope, since by definition it may not be well-formed, or consistent between the then/else block. (What if the else-block declared x as a typename?)
Bottom line, you'll need to move bar() into the if-body, or template foo() itself and have the type and value of x determined by your ....
In C++, it is possible to separate the declaration and definition of functions. For example, it is quite normal to declare a function:
int Foo(int x);
in Foo.h and implement it in Foo.cpp. Is it possible to do something similar with lambdas? For example, define a
std::function<int(int)> bar;
in bar.h and implement it in bar.cpp as:
std::function<int(int)> bar = [](int n)
{
if (n >= 5)
return n;
return n*(n + 1);
};
Disclaimer: I have experience with lambdas in C#, but I have not used them in C++ very much.
You can't separate declaration and definition of lambdas, neither forward declare it. Its type is a unique unnamed closure type which is declared with the lambda expression. But you could do that with std::function objects, which is designed to be able to store any callable target, including lambdas.
As your sample code shown you've been using std::function, just note that for this case bar is a global variable indeed, and you need to use extern in header file to make it a declaration (not a definition).
// bar.h
extern std::function<int(int)> bar; // declaration
and
// bar.cpp
std::function<int(int)> bar = [](int n) // definition
{
if (n >= 5) return n;
return n*(n + 1);
};
Note again that this is not separate declaration and definition of lambda; It's just separate declaration and definition of a global variable bar with type std::function<int(int)>, which is initialized from a lambda expression.
Strictly speaking you can't
Quoting from cpp reference
The lambda expression is a prvalue expression whose value is (until
C++17)whose result object is (since C++17) an unnamed temporary object
of unique unnamed non-union non-aggregate class type, known as closure
type, which is declared (for the purposes of ADL) in the smallest
block scope, class scope, or namespace scope that contains the lambda
expression
So the lambda is a unnamed temporary object. You can bind the lambda to a l-value object(for eg std::function) and by regular rules about variable declaration you can separate the declaration and definition.
Forward declaration is not the correct term because lambdas in C++ are objects, not functions. The code:
std::function<int(int)> bar;
declares a variable and you're not forced to assign it (that type has a default value of "pointer to no function"). You can compile even calls to it... for example the code:
#include <functional>
#include <iostream>
int main(int argc, const char *argv[]) {
std::function<int(int)> bar;
std::cout << bar(21) << "\n";
return 0;
}
will compile cleanly (but of course will behave crazily at runtime).
That said you can assign a lambda to a compatible std::function variable and adding for example:
bar = [](int x){ return x*2; };
right before the call will result in a program that compiles fine and generates as output 42.
A few non-obvious things that can be surprising about lambdas in C++ (if you know other languages that have this concept) are that
Each lambda [..](...){...} has a different incompatible type, even if the signature is absolutely identical. You for example cannot declare a parameter of lambda type because the only way would be to use something like decltype([] ...) but then there would be no way to call the function as any other []... form at a call site would be incompatible. This is solved by std::function so if you have to pass lambdas around or store them in containers you must use std::function.
Lambdas can capture locals by value (but they're const unless you declare the lambda mutable) or by reference (but guaranteeing the lifetime of the referenced object will not be shorter than the lifetime of the lambda is up to the programmer). C++ has no garbage collector and this is something needed to solve correctly the "upward funarg" problem (you can work-around by capturing smart pointers, but you must pay attention to reference loops to avoid leaks).
Differently from other languages lambdas can be copied and when you copy them you're taking a snapshot of their internal captured by-value variables. This can be very surprising for mutable state and this is I think the reason for which captured by-value values are const by default.
A way to rationalize and remember many of the details about lambdas is that code like:
std::function<int(int)> timesK(int k) {
return [k](int x){ return x*k; };
}
is basically like
std::function<int(int)> timesK(int k) {
struct __Lambda6502 {
int k;
__Lambda6502(int k) : k(k) {}
int operator()(int x) {
return x * k;
}
};
return __Lambda6502(k);
}
with one subtle difference that even lambda capturing references can be copied (normally classes containing references as members cannot).
Example:
#include <functional>
int main() {
auto test = []{};
test = []{};
return 0;
}
This emits the following error message in gcc 4.7.2:
test.cpp: In function ‘int main()’:
test.cpp:5:13: error: no match for ‘operator=’ in ‘test = <lambda closure object>main()::<lambda()>{}’
test.cpp:5:13: note: candidate is:
test.cpp:4:16: note: main()::<lambda()>& main()::<lambda()>::operator=(const main()::<lambda()>&) <deleted>
test.cpp:4:16: note: no known conversion for argument 1 from ‘main()::<lambda()>’ to ‘const main()::<lambda()>&’
From the standard 5.1.2.3 (emphasis mine):
An implementation may define the closure type differently from what is described below provided this does not alter the observable behavior of the program other than by changing:
— the size and/or alignment of the closure type,
— whether the closure type is trivially copyable (Clause 9)
— whether the closure type is a standard-layout class (Clause 9), or
— whether the closure type is a POD class (Clause 9).
As far as I can tell, this is what I'm running up against. It's attempting to use a deleted assignment operator and failing. I am curious to know if there's an easy workaround, and more broadly what the motivating rationale for allowing copy constructibility to be omitted for lambdas generally.
You seem to think that those two lambdas have the same type, but that is not true. Each one creates its own type:
#include <functional>
#include <type_traits>
#include <iostream>
int main() {
auto test = []{};
auto test2 = []{};
std::cout << std::is_same< decltype( test ), decltype( test2 ) >::value << std::endl;
return 0;
}
will print 0. Of course the error message you are getting from the compiler could be a little bit clearer in this regards...
The type of the
lambda-expression
(which is also the type of the closure object) is a unique, unnamed non-
union class type
So it is like you are doing the following:
struct {} a;
struct {} b;
a = b; // error, type mismatch
Use std::function if you want to assign different lambdas with the same signature to the same variable.
std::function<void()> f = []{};
f = []{}; //ok
Lambda can't be redefined because each lambda is of a different, anonymous, incompatible type.
They can be copied only if you pass them to a templated function (like std::function ctor) that would be able to deduce that type.
The reason you are not able to do this is because the copy assignment operator for the lambda-expression is declared deleted, See section 5.1.2/20 of the standard. For a more clear (for unusual definitions of clear) see this code sample
template<class T> void f(T x1)
{
T x2 = x1; // copy constructor exists, this operation will succeed.
x2 = x1; // assignment operator, deleted and will cause an error
}
int main()
{
f([]{});
return 0;
}
Other answers have pointed out that each lambda has a unique type, but this is not the reason why you are getting that error. This example shows that even if the two lambdas have the same type, it still is not able to copy it. However you are able to copy it to a new variable. This is the reason your error message is complaining about missing operator= and not about their types being different. Although each lambda having it's own type does not help you out much either.
If we could assign one lambda to another lambda of a different type, how do we copy the function bodies/definitions from that lambda to the other one? If we would be so stubborn, then we could use some member std::function-like type to be the one who will be copied. But that would be against the ol' C++ rule of not paying blah blah...
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Recursive lambda functions in c++0x
Why can't I call a lambda recursively if I write it as:
auto a = [&]
{
static int i = 0; i++;
std::cout << i << std::endl;
if (i<10)
a(); //recursive call
};
It gives compilation error (ideone):
prog.cpp:8:18: error: '((const main()::<lambda()>*)this)->main()::<lambda()>::a' cannot be used as a function
prog.cpp: In function 'int main()':
prog.cpp:9:9: error: variable 'auto a' with 'auto' type used in its own initializer
What does the error mean?
I understand the reason why I can't write this:
auto i=i+1; //error: unable to deduce 'auto' from '<expression error>'
We can't write this because the type of i has to be deduced from it's initialization, which means the type cannot be deduced if i itself appears in the initialization (ideone). But how does it matter in case of lambda? If I'm not wrong, the type of a lambda is determined by it's parameter(s) and the return type; it doesn't depend on the body if it returns nothing (in which case, the return type is deduced as void, irrespective of other statements in the lambda-body).
Anyway, I got a workaround, and I can use std::function instead as:
std::function<void()> a = [&]
{
static int i = 0; i++;
std::cout << i << std::endl;
if (i<10)
a();
};
which compile fines (ideone). But I'm still interested to know the reason why the auto version doesn't compile.
The reason is that there is no special case for lambda-expression initializers of auto variables.
Such special cases would be prone to errors and misuses. You need to define the rules when you propose that something like a() should work. How is the operator() looked up? What is the precise state of a's type? Will the type be complete? (which implies that you already know the capture list of the lambda). Once you have formulated that in a format reasonable for a spec, it would be easier to make statements on it.
Allowing your use case would mean yet another case where you need to scan ahead in code, because to determine the type of a in a() you must be sure that the initializer ends with nothing that could "unlambda" the type
struct y { void operator()() { } };
template<typename T> y operator+(T, y) { return y(); }
auto x = [] { x(); } + y();
In this case, x() would call y::operator(), not the lambda.
As it is now, a is simply forbidden to be mentioned in its entire initializer. Because in C++, auto is not a type. It is merely a type specifier standing for a to-be-deduced type. As a consequence, an expression can never have type auto.
As I see it the important difference between the auto a case and the std::function<void()> a case is that the type std::function<void()> doesn't know/care about what the type of the real function it refers to really is. Writing:
std::function<void()> a;
is perfectly fine, where as:
auto a;
makes little sense. So when the time comes to synthesize the capture if you use std::function<void()> all that needs to be known about the type is already known, whereas with auto it's not yet known.
In a recursive function f is defined by f and return type of f is also determined by f in case of auto so it leads to infinite recursion.
when auto tries to derive a type. decltype(f()) will further deduce to another decltype(f)` as f derives to f e.g. a call on anything recursive is recursive too. return type determination turns recursive when applied on a recursive function. in a recursive function end of the recursion may be done on runtime. but determination is static only
My previous question concluded that a distasteful "double cast" might be necessary to use the POSIX makecontext with a C++ lambda function (i.e. function object). Moving on, I'm now faced with a compilation error relating to the following minimal code:
#include <iostream>
#include <ucontext.h>
using namespace std;
template <typename T> void foo() {
ucontext_t c;
auto f = [=](int i){ cout << i << endl; };
makecontext(&c, (void (*) (void)) (void (*)(int)) f, 1, 12345);
}
int main(int argc, char *argv[]) {
foo<int>();
return 0;
}
The error is:
error: invalid cast from type ‘foo() [with T = int]::<lambda(int)>’ to type ‘void (*)(int)’
However, if I remove the unused (in this example) template argument from the foo function, so it becomes void foo();, and change the call to foo() the error disappears. Could someone tell me why? I'm using G++ 4.6.
Edit:
From the comments below, it seems the [=] in the code above causes the lambda to be a "capturing" lambda, regardless of the fact that it doesn't actually capture anything. The [=] is not necessary in my code, alas replacing with [] in GCC 4.6 does not remove the error. I am installing GCC 4.6.1 now...
If you use [=] to induce your lambda, you will not get a function pointer (or an object that is convertible to one). You will get a function object. And no amount of casting is going to allow you to pass that to makecontext. Not in any way that actually works.
According to N3291, the most recent working draft of C++0x:
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. The value returned by this conversion function shall be the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.
This is the only place where the specification allows conversion to a function pointer. Therefore, if recent versions of GCC do allow conversion to function pointers for [=], that not in accord with the specification.
Only captureless lambdas are convertible to function pointers; while f technically does not capture anything, it does have a default capture mode of capturing by value (for no apparent reason).
Change [=] to [] in the declaration of f and it should work as expected.
EDIT: The fact that this compiles with more recent versions of GCC (as noted by Kerrek) gives a strong indication that this is merely a compiler bug in the version you're using.