How can a std::reference_wrapper to a rvalue lambda work? - c++

In this article it says the following code is valid C++11 and works with GNU's libstdc++:
int n;
std::vector<int> v;
...
std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));
std::count_if(v.begin(), v.end(), f);
The thing is that I aways believed the lambda object to be created at call site, what would make it a temporary object in this snippet, since it is not being stored on any variable, but instead a const reference to it is being created and passed to the std::function. If that is so, the lambda object should have been destroyed right alway, leaving a dangling reference inside f, that would lead to undefined behavior when used by std::count_if.
Assuming the article is not wrong, what is wrong about my mental model? When the lambda object is destructed?

OK, let's start with the basics: the above code is certainly not legal because it is ill-formed in some rather basic ways. The line
std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));
would at bare minimum need to be written as
std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0;}));
Note that the code was written in the Dr.Dobb's article as it was in the question, i.e., any statement of the code being legal is already quite questionable.
Once the simple syntax errors are resolved the next question is whether std::cref() can actually be used to bind to an rvalue. The lambda exrpession is clearly a temporary according to 5.1.2 [expr.prim.lambda] paragraph 2 (thanks to DyP for the reference). Since it would generally be a rather bad idea to bind a reference to a temporary and is prohibited elsewhere, std::cref() would be a way to circumvent this restriction. It turns out that according to 20.10 [function.objects] paragraph 2 std::cref() is declared as
template <class T> reference_wrapper<const T> cref(const T&) noexcept;
template <class T> void cref(const T&&) = delete;
template <class T> reference_wrapper<const T> cref(reference_wrapper<T>) noexcept;
That is, the statement is incorrect even after correcting the syntax errors. Neither gcc nor clang compile this code (I have used fairly recent versions of both compilers with their respective standard C++ libraries). That is, based on the above declaration this code is clearly illegal!
Finally, there is nothing which would extend the life-time of the temporary in the above expression. The only reason the life-time of a temporary is extended is when it or one of its data members is immediately bound to a [const] reference. Wrapping a function call around the temporary inhibits this life-time extensions.
In summary: the code quoted in the article is not legal on many different levels!

I'm the author of the aforementioned article and I apologise for my mistake. No one else is to blame in this case. I've just ask the editor to add an errata:
1) Replace
std::count_if(v.begin(), v.end(), std::cref(is_multiple_of(n)));
with
is_multiple_of f(n);
std::count_if(v.begin(), v.end(), std::cref(f));
2) Replace
std::count_if(v.begin(), v.end(), std::cref([n](int i){return i%n == 0;}));
with
auto f([n](int i){return i%n == 0;});
std::count_if(v.begin(), v.end(), std::cref(f));
3) Replace
std::function<bool(int)> f(std::cref([n](int i) {return i%n == 0));
with
auto f1([n](int i){return i%n == 0;});
std::function<bool(int)> f(std::cref(f1));
In all cases the problem is the same (as Dietmar Kühl has nicely explained, +1 to him): we are calling std::cref on a temporary. This function returns a std::reference_wrapper storing a pointer to the temporary and this pointer will dangle if the std::reference_wrapper outlives the temporary. Basically this is what happens in case 3 above (which also contains a typo).
In cases 1 and 2, the std::reference_wrapper would not outlive the temporary. However, since the overloads of std::cref accepting temporaries (rvalues) are deleted the code should not compile (including case 3). At the time of publication, the implementations were not up to date with the Standard as they are today. The code used to compile but it doesn't when used with newer implementations of the standard library. This is not an excuse for my mistake though.
In any case, I believe the main point of the article, that is, the use of std::reference_wrapper, std::cref and std::ref to avoid expensive copies and dynamic allocations is still valid provided, of course, that the lifetime of the referred object is long enough.
Again, I apologise for the inconvenience.
Update: The article has been fixed. Thanks uk4321, DyP and, especially, lvella and Dietmar Kühl for raising and discussing the issue.

Related

lambda by-value capture in class member function shows strange behavior [duplicate]

I have a class which accumulates information about a set of objects, and can act as either a functor or an output iterator. This allows me to do things like:
std::vector<Foo> v;
Foo const x = std::for_each(v.begin(), v.end(), Joiner<Foo>());
and
Foo const x = std::copy(v.begin(), v.end(), Joiner<Foo>());
Now, in theory, the compiler should be able to use the copy elision and return-value optimizations so that only a single Joiner object needs to be created. In practice, however, the function makes a copy on which to operate and then copies that back to the result, even in fully-optimized builds.
If I create the functor as an lvalue, the compiler creates two extra copies instead of one:
Joiner<Foo> joiner;
Foo const x = std::copy(v.begin(), v.end(), joiner);
If I awkwardly force the template type to a reference it passes in a reference, but then makes a copy of it anyway and returns a dangling reference to the (now-destroyed) temporary copy:
x = std::copy<Container::const_iterator, Joiner<Foo>&>(...));
I can make the copies cheap by using a reference to the state rather than the state itself in the functor in the style of std::inserter, leading to something like this:
Foo output;
std::copy(v.begin(), v.end(), Joiner<Foo>(output));
But this makes it impossible to use the "functional" style of immutable objects, and just generally isn't as nice.
Is there some way to encourage the compiler to elide the temporary copies, or make it pass a reference all the way through and return that same reference?
You have stumbled upon an often complained about behavior with <algorithm>. There are no restrictions on what they can do with the functor, so the answer to your question is no: there is no way to encourage the compiler to elide the copies. It's not (always) the compiler, it's the library implementation. They just like to pass around functors by value (think of std::sort doing a qsort, passing in the functor by value to recursive calls, etc).
You have also stumbled upon the exact solution everyone uses: have a functor keep a reference to the state, so all copies refer to the same state when this is desired.
I found this ironic:
But this makes it impossible to use the "functional" style of immutable objects, and just generally isn't as nice.
...since this whole question is predicated on you having a complicated stateful functor, where creating copies is problematic. If you were using "functional" style immutable objects this would be a non-issue - the extra copies wouldn't be a problem, would they?
If you have a recent compiler (At least Visual Studio 2008 SP1 or GCC 4.4 I think) you can use std::ref/std::cref
#include <string>
#include <vector>
#include <functional> // for std::cref
#include <algorithm>
#include <iostream>
template <typename T>
class SuperHeavyFunctor
{
std::vector<char> v500mo;
//ban copy
SuperHeavyFunctor(const SuperHeavyFunctor&);
SuperHeavyFunctor& operator=(const SuperHeavyFunctor&);
public:
SuperHeavyFunctor():v500mo(500*1024*1024){}
void operator()(const T& t) const { std::cout << t << std::endl; }
};
int main()
{
std::vector<std::string> v; v.push_back("Hello"); v.push_back("world");
std::for_each(v.begin(), v.end(), std::cref(SuperHeavyFunctor<std::string>()));
return 0;
}
Edit : Actually, the MSVC10's implementation of reference_wrapper don't seem to known how to deduce the return type of function object operator(). I had to derive SuperHeavyFunctor from std::unary_function<T, void> to make it work.
Just a quick note, for_each, accumulate, transform (2nd form), provide no order guarantee when traversing the provided range.
This makes sense for implementers to provide mulit-threaded/concurrent versions of these functions.
Hence it is reasonable that the algorithm be able to provide an equivalent instance (a new copy) of the functor passed in.
Be wary when making stateful functors.
RVO is just that -- return value optimization. Most compilers, today, have this turned-on by default. However, argument passing is not returning a value. You possibly cannot expect one optimization to fit in everywhere.
Refer to conditions for copy elision is defined clearly in 12.8, para 15, item 3.
when a temporary class object that has
not been bound to a reference (12.2)
would be copied to a class object with
the same cv-unqualified type, the copy
operation can be omitted by
constructing the temporary object
directly into the target of the
omitted copy
[emphasis mine]
The LHS Foo is const qualified, the temporary is not. IMHO, this precludes the possibility of copy-elision.
For a solution that will work with pre-c++11 code, you may consider using boost::function along with boost::ref(as boost::reference_wrapper alone doesn't has an overloaded operator(), unlike std::reference_wrapper which indeed does). From this page http://www.boost.org/doc/libs/1_55_0/doc/html/function/tutorial.html#idp95780904, you can double wrap your functor inside a boost::ref then a boost::function object. I tried that solution and it worked flawlessly.
For c++11, you can just go with std::ref and it'll do the job.

What profit of universal reference usage in range based loop?

Many times I saw code like this:
template<typename Collection>
void Foo(Collection&& c)
{
for (auto&& i : std::forward<Collection>(c))
// do something with i
}
For all STL containers (except vector<bool>) i has type of lvalue reference. Is any practical sense to type auto&& in this case?
As you said, the perfect example of where you don't have an lvalue is std::vector<bool>. Using auto& will not compile, as a prvalue is returned from the iterator.
Also, it happened to me some times to make ranges that did not returned an lvalue from its iterator.
Also, the upside of using auto&& is that there is no cases where it won't work. Even of you have a bizarre case where your iterator yield a const rvalue reference, auto&& will bind to it.
For teaching, it's also easier to tell "use auto&& in your for loops." because it will not cause copy and work everywhere.
There where also a proposal to allow implicit auto&& and enable the syntax for (x : range) (I cannot remember which one is it. If you know it, please tell me in the comments)

Why does as_const forbid rvalue arguments?

I wanted to ask why as_const forbids rvalue arguments, according to cppreference.com (i.e. why the Standards folks made it so, not why cppreference.com specifically quoted them on that. And also not where in the spec the intent of the committee is codified, just for making sure :))). This (artificial) example would yield an error (user wants to make it const to keep COW quiet)
QChar c = as_const(getQString())[0];
Another question's answer notes that if we just remove the deletion of the rvalue reference overload, it would silently transform rvalues to lvalues. Right, but why not handle rvalues gracefully and return const rvalues for rvalue input and const lvalues for lvalue input?
The problem is to handle lifetime extension
const auto& s = as_const(getQString()); // Create dangling pointer
QChar c = s[0]; // UB :-/
A possibility would be the following overload (instead of the deleted one)
template< typename T >
const T as_const(T&& t) noexcept(noexcept(T(std::forward<T>(t))))
{
return std::forward<T>(t);
}
which involves extra construction, and maybe other pitfalls.
One reason might be that it could be dangerous on rvalues due to lack of ownership transfer
for (auto const &&value : as_const(getQString())) // whoops!
{
}
and that there might not be a compelling use case to justify disregarding this possibility.
(I accidentally answered the wrong question to a related question of this Q&A, after mis-reading it; I'm moving my answer to this question instead, the question which my answer actually addressed)
P0007R1 introduced std::as_const as part of C++17. The accepted proposal did not mention rvalues at all, but the previous revision of it, P0007R0, contained a closing discussion on rvalues [emphasis mine]:
IX. Further Discussion
The above implementation only supports safely re-casting an l-value as
const (even if it may have already been const). It is probably
desirable to have xvalues and prvalues also be usable with as_const,
but there are some issues to consider.
[...]
An alternative implementation which would support all of the forms
used above, would be:
template< typename T >
inline const T &
as_const( const T& t ) noexcept
{
return t;
}
template< typename T >
inline const T
as_const( T &&t ) noexcept( noexcept( T( t ) ) )
{
return t;
}
We believe that such an implementation helps to deal with lifetime
extension issues for temporaries which are captured by as_const, but
we have not fully examined all of the implications of these forms. We
are open to expanding the scope of this proposal, but we feel that
the utility of a simple-to-use as_const is sufficient even without the
expanded semantics.
So std::as_const was basically added only for lvalues as the implications of implementing it for rvalues were not fully examined by the original proposal, even if the return by value overload for rvalue arguments was at least visited. The final proposal, on the other hand, focused on getting the utility in for the common use case of lvalues.
P2012R0 aims to address the hidden dangers of range-based for loops
Fix the range‐based for loop, Rev0
The range-based for loop became the most important control structure
of modern C++. It is the loop to deal with all elements of a
container/collection/range.
However, due to the way it is currently defined, it can easily
introduce lifetime problems in non-trivial but simple applications
implemented by ordinary application programmers.
[...]
The symptom
Consider the following code examples when iterating over elements of
an element of a collection:
std::vector<std::string> createStrings(); // forward declaration
…
for (std::string s : createStrings()) … // OK
for (char c : createStrings().at(0)) … // UB (fatal runtime error)
While iterating over a temporary return value works fine, iterating
over a reference to a temporary return value is undefined behavior.
[...]
The Root Cause for the problem
The reason for the undefined behavior above is that according to the
current specification, the range-base for loop internally is expanded
to multiple statements: [...]
And the following call of the loop:
for (int i : createOptInts().value()) … // UB (fatal runtime error)
is defined as equivalent to the following:
auto&& rg = createOptInts().value(); // doesn’t extend lifetime of returned optional
auto pos = rg.begin();
auto end = rg.end();
for ( ; pos != end; ++pos ) {
int i = *pos;
…
}
By rule, all temporary values created during the initialization of the
reference rg that are not directly bound to it are destroyed before
the raw for loop starts.
[...]
Severity of the problem
[...]
As another example for restrictions caused by this problem consider
using std::as_const() in a range-based for loop:
std::vector vec; for (auto&& val : std::as_const(getVector())) {
… }
Both std::ranges with operator | and std::as_const() have a
deleted overload for rvalues to disable this and similar uses. With
the proposed fix things like that could be possible. We can definitely
discuss the usability of such examples, but it seems that there are
more example than we thought where the problem causes to =delete
function calls for rvalues.
These gotchas is one argument to avoid allowing an std::as_const() overload for rvalues, but if P2012R0 gets accepted, such an overload could arguably be added (if someone makes a proposal and shows a valid use case for it).
Because as_const doesn't take the argument as const reference.
Non-const lvalue references can't bind to temporaries.
The forwarding reference overload is explicitly deleted.

Passing temporaries as LValues

I'd like to use the following idiom, that I think is non-standard. I have functions which return vectors taking advantage of Return Value Optimization:
vector<T> some_func()
{
...
return vector<T>( /* something */ );
}
Then, I would like to use
vector<T>& some_reference;
std::swap(some_reference, some_func());
but some_func doesn't return a LValue. The above code makes sense, and I found this idiom very useful. However, it is non-standard. VC8 only emits a warning at the highest warning level, but I suspect other compilers may reject it.
My question is: Is there some way to achieve the very same thing I want to do (ie. construct a vector, assign to another, and destroy the old one) which is compliant (and does not use the assignment operator, see below) ?
For classes I write, I usually implement assignment as
class T
{
T(T const&);
void swap(T&);
T& operator=(T x) { this->swap(x); return *this; }
};
which takes advantage of copy elision, and solves my problem. For standard types however, I really would like to use swap since I don't want an useless copy of the temporary.
And since I must use VC8 and produce standard C++, I don't want to hear about C++0x and its rvalue references.
EDIT: Finally, I came up with
typedef <typename T>
void assign(T &x, T y)
{
std::swap(x, y);
}
when I use lvalues, since the compiler is free to optimize the call to the copy constructor if y is temporary, and go with std::swap when I have lvalues. All the classes I use are "required" to implement a non-stupid version of std::swap.
Since std::vector is a class type and member functions can be called on rvalues:
some_func().swap(some_reference);
If you don't want useless copies of temporaries, don't return by value.
Use (shared) pointers, pass function arguments by reference to be filled in, insert iterators, ....
Is there a specific reason why you want to return by value?
The only way I know - within the constraints of the standard - to achieve what you want are to apply the expression templates metaprogramming technique: http://en.wikipedia.org/wiki/Expression_templates Which might or not be easy in your case.

Prevent unnecessary copies of C++ functor objects

I have a class which accumulates information about a set of objects, and can act as either a functor or an output iterator. This allows me to do things like:
std::vector<Foo> v;
Foo const x = std::for_each(v.begin(), v.end(), Joiner<Foo>());
and
Foo const x = std::copy(v.begin(), v.end(), Joiner<Foo>());
Now, in theory, the compiler should be able to use the copy elision and return-value optimizations so that only a single Joiner object needs to be created. In practice, however, the function makes a copy on which to operate and then copies that back to the result, even in fully-optimized builds.
If I create the functor as an lvalue, the compiler creates two extra copies instead of one:
Joiner<Foo> joiner;
Foo const x = std::copy(v.begin(), v.end(), joiner);
If I awkwardly force the template type to a reference it passes in a reference, but then makes a copy of it anyway and returns a dangling reference to the (now-destroyed) temporary copy:
x = std::copy<Container::const_iterator, Joiner<Foo>&>(...));
I can make the copies cheap by using a reference to the state rather than the state itself in the functor in the style of std::inserter, leading to something like this:
Foo output;
std::copy(v.begin(), v.end(), Joiner<Foo>(output));
But this makes it impossible to use the "functional" style of immutable objects, and just generally isn't as nice.
Is there some way to encourage the compiler to elide the temporary copies, or make it pass a reference all the way through and return that same reference?
You have stumbled upon an often complained about behavior with <algorithm>. There are no restrictions on what they can do with the functor, so the answer to your question is no: there is no way to encourage the compiler to elide the copies. It's not (always) the compiler, it's the library implementation. They just like to pass around functors by value (think of std::sort doing a qsort, passing in the functor by value to recursive calls, etc).
You have also stumbled upon the exact solution everyone uses: have a functor keep a reference to the state, so all copies refer to the same state when this is desired.
I found this ironic:
But this makes it impossible to use the "functional" style of immutable objects, and just generally isn't as nice.
...since this whole question is predicated on you having a complicated stateful functor, where creating copies is problematic. If you were using "functional" style immutable objects this would be a non-issue - the extra copies wouldn't be a problem, would they?
If you have a recent compiler (At least Visual Studio 2008 SP1 or GCC 4.4 I think) you can use std::ref/std::cref
#include <string>
#include <vector>
#include <functional> // for std::cref
#include <algorithm>
#include <iostream>
template <typename T>
class SuperHeavyFunctor
{
std::vector<char> v500mo;
//ban copy
SuperHeavyFunctor(const SuperHeavyFunctor&);
SuperHeavyFunctor& operator=(const SuperHeavyFunctor&);
public:
SuperHeavyFunctor():v500mo(500*1024*1024){}
void operator()(const T& t) const { std::cout << t << std::endl; }
};
int main()
{
std::vector<std::string> v; v.push_back("Hello"); v.push_back("world");
std::for_each(v.begin(), v.end(), std::cref(SuperHeavyFunctor<std::string>()));
return 0;
}
Edit : Actually, the MSVC10's implementation of reference_wrapper don't seem to known how to deduce the return type of function object operator(). I had to derive SuperHeavyFunctor from std::unary_function<T, void> to make it work.
Just a quick note, for_each, accumulate, transform (2nd form), provide no order guarantee when traversing the provided range.
This makes sense for implementers to provide mulit-threaded/concurrent versions of these functions.
Hence it is reasonable that the algorithm be able to provide an equivalent instance (a new copy) of the functor passed in.
Be wary when making stateful functors.
RVO is just that -- return value optimization. Most compilers, today, have this turned-on by default. However, argument passing is not returning a value. You possibly cannot expect one optimization to fit in everywhere.
Refer to conditions for copy elision is defined clearly in 12.8, para 15, item 3.
when a temporary class object that has
not been bound to a reference (12.2)
would be copied to a class object with
the same cv-unqualified type, the copy
operation can be omitted by
constructing the temporary object
directly into the target of the
omitted copy
[emphasis mine]
The LHS Foo is const qualified, the temporary is not. IMHO, this precludes the possibility of copy-elision.
For a solution that will work with pre-c++11 code, you may consider using boost::function along with boost::ref(as boost::reference_wrapper alone doesn't has an overloaded operator(), unlike std::reference_wrapper which indeed does). From this page http://www.boost.org/doc/libs/1_55_0/doc/html/function/tutorial.html#idp95780904, you can double wrap your functor inside a boost::ref then a boost::function object. I tried that solution and it worked flawlessly.
For c++11, you can just go with std::ref and it'll do the job.