After some searching and testing, I have learned the following facts about lambda expression. 1) when we write a lambda expression, the compiler would create an anonymous function object for it, and make it as an instance of the function object; 2) the captured variables of a lambda expression will be used to initialize the member data of the created function object; 3) when we store a lambda function, we actually get a named instance of the function object; 4) a generic lambda function is actually a function object template; 5) a stored (plain and even generic) lambda expression can be declared and defined with template; and 6) a stored lambda expression template can even be partially specialized, just as function objects.
Given all the features of lambdas stated above, it seems to me that, through lambdas, we are able to do whatever we used to do with function objects, and regarding efficiency, they should have same performance.
On the other hand, lambdas also have additional advantages: 1) a lambda expression is more understandable than a function object, especially for inline, short functions; and 2) defining a stored lambda can be seen as a kind of syntactic sugar for defining a function object, and making an instance of it.
Therefore, for me, it seems that we have no reasons to define a function object manually any more.
Of course, I also have some concern for substituting lambdas for function objects universally, like 1) for functions more than, say, 10 lines, defining them as a stored lambda may be unusual (or even awkward, I don't know), and 2) defining lambdas at file-level may (or may not, I am not quite sure) cause some unexpected problems.
Here are my questions: is it wise to prefer lambdas to function objects? Is there any other advantages that a function object have but lambdas not? Is my concern reasonable? And, is there any other concern that I should notice when using lambdas instead of FO universally?
Thanks for any reply!
Lambdas are a terse syntax for certain kinds of function objects.
They cannot be trivially constructed, they can have exactly one (possibly template) operator(), and their type cannot be named without first having access to an instance and using decltype.
As of C++14, they are not constexpr friendly, and they are not guaranteed to be trivially copyable even if their state should be.
Two lambdas with the same capture types and method do not share a type unless declared at the same spot; this can cause symbol bloat.
You cannot declare other operations in a lambda besides (), like friend bool operator< or ==, or whatever.
Given these restructions, sure, use lambdas. Terseness has lots of utility.
Related
C++20 introduces DefaultConstructible lambdas. However, cppreference.com states that this is only for stateless lambdas:
If no captures are specified, the closure type has a defaulted default constructor. Otherwise, it has no default constructor (this includes the case when there is a capture-default, even if it does not actually capture anything).
Why does this not extend to lambdas that capture things that are DefaultConstructible? For instance, why can [p{std::make_unique<int>(0)}](){ return p.get(); } not be DefaultConstructible, where the captured p would be nullptr?
Edit: For those asking why we would want this, the behavior only seems natural because one is forced to write something like this when calling standard algorithms that require functors to be default-constructible:
struct S{
S() = default;
int* operator()() const { return p.get(); }
std::unique_ptr<int> p;
};
So, we can pass in S{std::make_unique<int>(0)}, which does the same thing.
It seems like it would be much better to be able to write [p{std::make_unique<int>(0)}](){ return p.get(); } versus creating a struct that does the same thing.
There are two reasons not to do it: conceptual and safety.
Despite the desires of some C++ programmers, lambdas are not meant to be a short syntax for a struct with an overloaded operator(). That is what C++ lambdas are made of, but that's not what lambdas are.
Conceptually, a C++ lambda is supposed to be a C++ approximation of a lambda function. The capture functionality is not meant to be a way to write members of a struct; it's supposed to mimic the proper lexical scoping capabilities of lambdas. That's why they exist.
Creating such a lambda (initially, not by copy/move of an existing one) outside of the lexical scope that it was defined within is conceptually vacuous. It doesn't make sense to write a thing bound to a lexical scope, then create it outside of the scope it was built for.
That's also why you cannot access those members outside of the lambda. Because, even though they could be public members, they exist to implement proper lexical scoping. They're implementation details.
To construct a "lambda" that "captures variables" without actually capturing anything only makes sense from a meta-programming perspective. That is, it only makes sense when focusing on what lambdas happen to be made of, rather than what they are. A lambda is implemented as a C++ struct with captures as members, and the capture expressions don't even technically have to name local variables, so those members could theoretically be value initialized.
If you are unconvinced by the conceptual argument, let's talk safety. What you want to do is declare that any lambda shall be default constructible if all of its captures are non-reference captures and are of default constructible types. This invites disaster. Why?
Because the writer of many such lambdas didn't ask for that. If a lambda captures a unique_ptr<T> by moving from a variable that points to an object, it is 100% valid (under the current rules) for the code inside that lambda to assume that the captured value points to an object. Default construction, while syntactically valid, is semantically nonsense in this case.
With a proper named type, a user can easily control if it is default constructible or not. And therefore, if it doesn't make sense to default construct a particular type, they can forbid it. With lambdas, there is no such syntax; you have to impose an answer on everyone. And the safest answer for capturing lambdas, the one that is guaranteed to never break code, is "no."
By contrast, default construction of captureless lambdas can never be incorrect. Such functions are "pure" (with respect to the contents of the functor, since the functor has no contents). This also matches with the above conceptual argument: a captureless lambda has no proper lexical scope and therefore spawning it anywhere, even outside of its original scope, is fine.
If you want the behavior of a named struct... just make a named struct. You don't even need to default the default constructor; you'll get one by default (if you declare no other constructors).
There are several ways to pass callable objects as parameters or to store them for future use. You can create a class with operator(), you can define a function and pass a pointer to it, and, since C++11, you can define a lambda via [](){} syntax.
I appreciate lambda syntax as a shortcut in expressions such as find_if that often beg for a compact callable expression. What I don't understand about lambda is the desire to use them outside the point of their declaration and risk introducing dangling references and such. C++ already has a powerful way to pass callable objects around which is much safer then lambda, and in those situation there is no benefit of compact expression of lambda.
Thus the question: why does C++11 allow use of a lambda outside the function that declares is or the functions called from it (and therefore introduces the risk of dangling references, etc)? Could you give an example where keeping lambda live outside the declaring function would be desirable?
Consider a function which is registered to be called when a future event occurs. It would be convenient to define it as a lambda, but it has to live beyond the scope in which it is defined:
for example
m_button->setOnClick(YOUR LAMBDA GOES HERE);
What I don't understand about lambda is the desire to use them outside the point of their declaration and risk introducing dangling references and such. C++ already has a powerful way to pass callable objects around which is much safer then lambda, and in those situation there is no benefit of compact expression of lambda.
(1) Lambda isn't implicitly less safe than any other way of defining function objects. The way of passing a lambda is exactly the same as passing an instance of a named functor.
You can store references in a named functor, and you can capture references in a lambda. Storing a reference to a local object in either of those cases is a severe bug if the function object out lives the scope where those references were bound.
Whether the syntax of lambda is beneficial or not, is a matter of preference. I suppose, one could argue that because lambdas make the definition of functors simpler, it also makes the definition of broken functors simpler.
why does C++11 allow use of a lambda outside the function that declares is or the functions called from it (and therefore introduces the risk of dangling references, etc)?
Firstly, I imagine such semantic limitation would be hard to implement. You can't make them non-copyable because that would make them useless in standard algorithms.
Secondly, because storing a function object for later use is useful, see (2) and using lambdas isn't more dangerous than using instances of named functors, see (1).
Could you give an example where keeping lambda live outside the declaring function would be desirable?
(2) Just about any asynchronous callback situation. std::async, std::thread, GUI and other event systems. Callable function objects will be stored for later use in those situations and typically the objects do outlive the scope where they were created.
In general and also in this case, lambdas advantages over named functor types is that you get to place the function definition right where it's used. Well, you can never have the definition where it's actually used in a generic situation of asynchronous callbacks, but the point of registering the callback is as close as you can get.
The disadvantage of lambdas is their hard-for-humans-to-parse syntax that is an explosion of different brackets, braces and parenthesis. Again, this is matter of preference.
C++'s lambdas would be convenient to use in templates that need function objects but alas, they cannot be default constructed.
As discussed in this question, this makes sense for lambdas that have a non-empty capture-list.
Instantiating C++ lambda by its type
Kerrek explains:
The code doesn't make sense. Imagine you have a capturing lambda like
this:
{
int n = 0;
auto t = [&n](int a) -> int { return n += a; };
}
What could it possibly mean to default-construct an object of type
decltype(t)?
What about lambdas with an empty capture-list? Is there a reason those also don't make sense to default construct? Is there anything more to it than "the standard says so"?
In general, lambdas where specified as little as possible to solve specific use cases.
Other possibly useful things, like "a lambda that only copies trivially copyable data must be trivially copyable" was also omitted. (The standard does not specify if a lambda is trivially copyable or not)
The upside is that it makes lambda an easier to implement feature, which is important. The downside is that this rules out certain uses that are not in the "intended" set.
If you think that "a capture free lambda must have a zero-argument constructor" is an important thing, propose it. But this takes one use of lambdas (easy local capture and creation of function objects) and morphs it into something else (easy creation of stateless function objects whose types can be passed around).
In Effective STL, Scott Meyers back in 2001 advises:
Item 46: Consider function objects instead of functions as algorithm parameters
In said chapter, he proceeds to explain that inline operator() can get inlined into the algorithm's body, but passing a function generally can`t. This is because we are passing a function pointer actually.
In support of that I seem to remember that if a function's address is ever taken, the function can't be inlined.
So two questions here. Firstly, is this still true with C++14?
If yes:
Why is there no mechanism to do this automatically (motivation: declaring a functor is a lot less straightforward and readable, than declaring a function).
A lambda without capture is convertible to function pointer, while a capturing lambda can only be passed as a functor. Does this mean we need to capture something only for the sake of the stated optimization?
Is this still true with C++14?
Depends on whether the compiler can inline the whole algorithm. If it can, then it can probably also inline the function call. If not, then the function probably can't be inlined, because the algorithm in that case is instantiated using a function pointer type and so must be able to handle all function pointers of that type.
For instance, g++ can inline a simple algorithm like std::transform but not std::sort.
A lambda without capture is convertible to function pointer, while a capturing lambda can only be passed as a functor. Does this mean we need to capture something only for the sake of stated at top optimization?
No. A lambda without capture is still a functor; the algorithm is instantiated using the closure type (the type of the lambda) rather than the function pointer type.
In support of that I seem to remember that if a function's address is ever taken, the function can't be inlined.
You're reading that incorrectly. A function can be inlined into any direct call site, and any indirect call site if the compiler can trace the function pointer. What the GCC manpage is saying that a function that is inlined into every call site will not be emitted as a separate function at all (thus reducing binary size), unless its address is taken.
Firstly, is this still true with C++14?
Yes. Of course, now you would generally write lambdas instead of hand-crafted functors.
Why is there no mechanism to do this automatically.
It's a matter of the type system. All functions with a given signature have the same type. Thus, an algorithm that is passed a function pointer gets instantiated to one concrete function, and its code just exists once in the compilation model of C++.
Of course, an optimizer can still specialize the function on one particular argument, but that's a more advanced optimization than just inlining a functor. So yes, there is a mechanism, it's just less likely to be used.
Does this mean we need to capture something only for the sake of stated at top optimization?
No. The conversion to a function pointer is possible, but unless you invoke it explicitly, it won't be done when you pass a lambda to an algorithm.
I'm confident I get the general gist of the constructs, but I can't see the purpose of them in c++. I have read the previous posts on the topic here on SO and elsewhere, but I fail to see why they should be a new language feature.
The things I would like answered is thusly
What is the difference between a lambda and a template argument accepting a function/functor.
Is a closure just a functor with some set object state (scope?)?
What is the "killer app" for these constructs? or perhaps the typical use case?
Lambdas are really just syntactic sugar for a functor. You could do it all yourself: defining a new class, making member variables to hold the captured values and references, hooking them up in the constructor, writing operator()(), and finally creating an instance and passing it. Or you could use a lambda that's 1/10 as much code and works the same.
Lambdas which don't capture can be converted to function pointers. All lambdas can be converted to std::function, or get their own unique type which works well in templated algorithms accepting a functor.
Ok, you're actually asking a bunch of different questions, possibly because you are not fully familiar with terminology. I'll try to answer all.
What's the difference between a lambda and "operator()"? - Let's reword this to, "What's the difference between a lambda and object with operator()?"
Basically, nothing. The main difference is that a lambda expression creates a functional object while an object with an operator() IS a functional object. The end result is similar enough to consider the same though, an entity that can be invoked with the (params) syntax.
What's the difference between a closure and a functor? This is also rather confused. Please review this link:
http://en.wikipedia.org/wiki/Closure_(computer_programming)
http://en.wikipedia.org/wiki/Closure_(computer_programming)#C.2B.2B
So, as you can see, a closure is a kind of "functor" that is defined within a scope such that it absorbs the variables available to it within that scope. In other words, its a function that is built on the fly, during the operation of the program and that building process is parameterized by the runtime values of the scope containing it. So, in C++ closures are lambdas that use values within the function building the lambda.
What is the difference between a lambda and a template argument accepting a function/functor? - This is again confused. The difference is that they are nothing alike, really. A template "argument" accepting a function/functor is already confused wording so I'll assume by "argument" you mean "function", because arguments don't accept anything. In this case, although a lambda can accept a functor as an argument, it can't be templated, one. Two, generally the lambda is the one being passed as an argument to a function accepting a functor argument.
Is a closure just a functor with some set object state (scope?)?
As you can see by the above link, no. In fact, a closure doesn't even have state, really. A closure is built based UPON the state of some other entity that built it, within that functor though this isn't state, it's the very construction of the object.
What is the "killer app" for these constructs? or perhaps the typical use case?
I'll reword that to, "Why are these things useful?"
Well, in general the ability to treat any object as a function if it has operator() is extremely useful for a whole array of things. For one, it allows us to extend the behavior of any stdlib algorithm by using either objects or free functions. It's impossible to inummerate the vast supply of usefulnesses this has.
More specifically speaking of lambda expressions, they simply make this process yet easier. The limitations imposed by object definitions made the process of using stdlib algorithms slightly inefficient in some cases (from a development use perspective, not program efficiency). For one thing, at the time at least any object passed as a parameter to a template had to be externally defined. I believe that's also changing, but still...having to create entire objects just to perform basic things, that only get used in one place, is inconvenient. Lambda expressions allow that definition to be quite easy, within the scope of the place its being used, etc, etc...