quick question on lambda functions. I saw that when using the gcc complier there is an implicit cast to a function pointer for the operator() function of the lambda if it doesn't have a capture. As a result looking at this watered down example below what should I expect the life time of this function to be available for callback? What scope is the lambda function defined at? local scope, package scope, global scope? Is there any problem with calling the lambda after the function exits in this case? I am not looking to use std::function here at all, so please don't respond with a std::function return value for a solution. My question is really just to understand the scope of that function (without capture) and if it is available for the lifetime of the program.
#include <iostream>
typedef int(*fPtrT)(int,int);
fPtrT fx() {
return static_cast<fPtrT>([](int i, int j){return i+j;});
}
int main()
{
std::cout << fx()(5,2) << std::endl;
}
A lambda expression returns an instance of an anonymously-defined class (the type is known only to the compiler). This class overloads the operator () to serve as a function object.
On top of that, lambda expressions that don't close over any state have the added specification that they can be implicitly converted to a C-style function pointer. In those cases, imagine that the operator () just invokes a static function (and the implicit conversion is a pointer to that static function).
Knowing all of this, we can say the following things about the code you posted:
Every time the fx function is invoked, an instance of the anonymous class is created
This instance is an r-value; as such it only exists until the end of the statement
The function pointer returned by the fx function is effectively a pointer to a static function
The function pointer can be safely used by whoever has possession of it (because it is a static function with no shared state)
A lambda that doesn't capture anything is safe to use anywhere.
If it captures by reference then it's only safe within the lifetime of the objects it refers to.
If it captures by value then it's safe anywhere – it's just an object.
Related
I've got this code snippet:
int x = 3;
auto fauto = [=](){ cout<<'x'; };
function<void()> func{fauto};
func();
void (*rawPf)() = fauto; // fail to compile
rawPf();
I knew the syntax that only non-capture lambda can be assigned to function pointer. But:
(1) Why std::function can hold capture-lambda?
(2) as both std::function and function pointers are callable, what's the core difference that makes std::function able to hold capture-lambda, while function pointer cannot?
Any detailed explanation on language design for this?
Why can a function pointer not hold a lambda with a capture : because a Lambda is NOT a function ,it's an object!
Why can a lambda without a capture be converted to a function pointer ?
A Lambda is just an ordinairy object (a piece of data) of a compiler generated class (with a unique classname that only the compiler knows) with a function-operator member (i.e. auto operator() ( ??? )) that the compiler defines for you with the parameter definitions (if any) you provide. The data-members of a lambda-object are defined by the capture-list and/or usage of variables of its enclosing scope.
All non-static member functions when called on an object get a implicit hidden argument called this. This is also the case when you 'call' the lambda.
Now ,when you don't capture something ,the lambda has no data (empty class) and the compiler doesn't have to generate an implicit this pointer for the call ,which makes the function operator just like an ordinairy function and the compiler can convert it to function pointer.
So it not the lambda that is converted to a function-pointer ,it's the lambda's function-operator that is converted.
Why can std::function hold both : because it's a template and with templates and specializations you can do almost anything.
There's no reason not to implement it in std::function<>, so it's implemented.
The reason a function pointer cannot hold a capture lambda is that the latter has a this pointer and there's no place to store that in the function pointer (well, unless you compile new code or assign it from a pool). However, std::function<> can allocate arbitrary amount of space, amongst these, the this pointer.
You can understand this better if you try to implement a function returning function pointers for lambdas (which, when called, by specification should call the lambda).
This question already has answers here:
std::thread pass by reference calls copy constructor
(2 answers)
Closed 5 months ago.
(1) I've this code snippet:
void tByRef(int& i) {
++i;
}
int main() {
int i = 0;
tByRef(i); // ok
thread t1(tByRef, i); // fail to compile
thread t2(tByRef, (int&)i); // fail to compile
thread t3(tByRef, ref(i)); // ok
return 0;
}
As you could see, function tByRef accepts a lvalue reference as parameter to change the i value. So calling it directly tByRef(i) passes compilation.
But when I try to do same thing for thread function call, e.g. thread t1(tByRef, i), it fails to compile. Only when I added ref() around i, then it gets compiled.
Why need extra ref call here? If this is required for passing by reference, then how to explain that tByRef(i) gets compiled?
(2) I then changed tByRef to be template function with && parameter, this time, even t3 fails to compile:
template<typename T>
void tByRef(T&& i) {
++i;
}
This && in template parameter type is said to be reference collapse which could accept both lvalue and rvalue reference. Why in my sample code, t1, t2, t3 all fails to compile to match it?
Thanks.
Threads execute asynchronously from the code that started them. That's kind of the point. This means that, when a thread function actually gets called, the code that started the thread may well have left that callstack. If the user passed a reference to a local variable, that variable may be off the stack by the time the thread function gets called. Basically, passing by reference to a thread function is highly dangerous.
However, in C++, passing a variable by reference is trivial; you just provide the name to the function that takes its parameter by reference. Since it is so dangerous in this particular case, std::thread takes steps to prevent you from doing it.
All arguments to the thread function are copied/moved into internal storage when the thread object is created, and your thread function's parameters are initialized from those copies.
Now, thread could initialize non-const lvalue reference parameters with a reference to the internal object for that parameter. However, a function which specifically takes a non-const lvalue reference is almost always a function that is expected to modify this value in a way that will be visible to others. But... it won't be visible to anyone, because it will be given a reference to an object stored internally in the thread that is accessible to no one else.
In short, whatever you thought was going to happen will not happen. Hence the compile error: thread is specifically designed to detect this circumstance and assume that you've made some kind of mistake.
However, while non-const lvalue reference parameters are inherently dangerous, they can still be useful. So std::ref is used as a way for a user to explicitly ask to pass a reference parameter.
As for why it fails to compile in your second example, tByRef in this case is not the name of a function. It is the name of a template. std::thread expects to be given a value which it can call. A template is not a value, nor is it convertible to a value.
A function template is a construct which generates a function when provided with template parameters. The template name alone is not a function.
1)
Because you need a object to pass to another thread, hence you need to encapsulate the variable with an object that mimics a reference, but isn't truly a reference.
std::ref docs.
Note the possible implementation section in the std::reference_wrapper documentation, it saves a pointer to the object.
2)
C++ needs a way to deduct the template type, the compiler creates a new function for each different template. In this case you must specify the type:
std::thread t3(tByRef<std::reference_wrapper<int>>, std::ref(i));
Have a look at the code example listed below, I tested it with Compiler explorer (using gcc and clang) and it works and prints out the (expected) output of 200.
What I am trying to figure out: why exactly is this valid C++. Or is it not?
Here, I'm using the using keyword to define an alias (instead of a typedef) for the function type ft, which describes functions that take an int as argument and return an int (ft is a function type, not a function pointer type!). The equivalent typedef syntax is typedef int ft(int);. Since the C++ standard says that for every typedef there is an equivalent form using the using syntax, it is clear that using ft = int(int) is a well-defined way to specify this function type.
Now comes the interesting part: I use the type ft to specify the argument type of another function (print_it), and then call it, passing a lambda function. My question is: where/how does the C++ standard say that this should actually work? I think it is a wonderfully simple way to pass lambdas around. That this works is not clear to me, as a lambda is really a functor, not a function in the strict sense. So I think it is not clear that this lambda matches the type ft and therefore can be passed to print_it (if ft was defined to be std::function<int(int)> it would be clear though).
The code example:
#include <iostream>
using ft = int(int);
void print_it (ft f, int a)
{
std::cout << f(a) << std::endl;
}
int main ()
{
auto my_lambda = [] (int a) -> int { return 2 * a; };
print_it (my_lambda, 100);
return (0);
}
My question is: where/how does the C++ standard say that this should actually work?
This is specified in [expr.prim.lambda.closure]/7, which specifies that a lambda's closure type has a conversion function to a function pointer type matching the lambda's parameter and return types if the lambda is non-generic and doesn't have any capture. Calling through this function pointer basically behaves as if the lambda body was just a normal function to which the pointer points, which is possible because there are no captures which could give the lambda a state that a normal function can't have.
This applies here and you are using the conversion operator to implicitly convert the lambda object to a function pointer when passing it to print_it. This works since the lambda's parameter and return type matches the ft type and a function type used as type of a function parameter is adjusted to a pointer-to-function type instead. (See [dcl.fct]/5 for the last part.)
For generic lambdas there is a conversion function template (see the following standard paragraph). For lambdas with a capture there is no such conversion function and therefore this wouldn't work for them.
This only works because your lambda does not capture. A capture-less lambda can be converted to a C-style function pointer but a captureing lambda cannot.
I'm trying to write a fairly simple method that returns a future. A lambda sets the future. This is a minimal example. In reality the lambda might be invoked in a different thread, etc.
#include <future>
std::future<std::error_code> do_something() {
std::promise<std::error_code> p;
auto fut = p.get_future();
auto lambda = [p = std::move(p)] {
std::error_code error;
p.set_value(error);
};
lambda();
return std::move(fut);
}
int main() { return do_something().get().value(); }
For some reason I get a type error. VSCode intellisense says:
no instance of overloaded function "std::promise<_Ty>::set_value [with _Ty=std::error_code]" matches the argument list and object (the object has type qualifiers that prevent a match) -- argument types are: (std::remove_reference_t<std::error_code &>) -- object type is: const std::remove_reference_t<std::promise<std::error_code> &>
And MSVC compiler says:
error C2663: 'std::promise<std::error_code>::set_value': 2 overloads have no legal conversion for 'this' pointer
I really don't understand the VS Code error. Is it saying that it thinks error is a const promise<error_code>? How do I correctly call set_value on a promise which was moved inside a lambda's capture?
By default lambda stores all its captured values (non-references) as const values, you can't modify them. But lambda supports keyword mutable, you can add it like this:
[/*...*/](/*...*/) mutable { /*...*/ }
This will allow inside body of a lambda to modify all its values.
If for some reason you can't use mutable, then you can use other work-around:
[/*...*/, p = std::make_shared<ClassName>(std::move(p)), /* ... */](/*...*/) {/*...*/}
In other words wrap your moved value into std::shared_ptr, you can also use std::unique_ptr if you like.
Wrapping into shared pointer solves the problem, because shared pointer (unique also) allows to modify its underlying object value even if pointer itself is const.
Don't forget inside the body of a lambda to dereference p as a pointer, in other words if you used p.SomeMethod(), now you have to use p->SomeMethod() (with -> operator).
The C++11 standard says (or at least, the version I have - not the final one) :
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.
I understand why it is not possible to get a function pointer from a stateful lambda since a function pointer cannot hold any data by itself.
But when the captured objects are just a static members /static variable, there is no such limitation since the references to the captured objects can be hardwired in the function itself.
struct A {
static int count = 0;
void foo() {
static int bar = 0;
auto fun = [&]()->void {
count++;
bar++;
};
void(*ptrFun)();
ptrFun = fun; // forbidden by the quoted wording
}
};
Why isn't it always possible to convert a lambda to a function pointer as soon as the former is stateless ? Am I missing something or does the committee forgot this specific point ?
A::count does not need to be captured at all. Only this and local variables need to be captured. Variables with static storage duration (e.g., static data members, namespace-scope variables, or function-local static variables) do not need to be captured because they are "unique." There is exactly one instance of each such variable, so a reference to the object does not need to be captured.
If you remove the default capture from your lambda (i.e., change [&] to []) and define count, it should compile without error. (I've verified that both Visual C++ 2012 RC and g++ 4.5.1 accept the code; the only change I had to make was to move the inline initialization of count, since neither of those compilers supports that C++11 feature yet.)