The following code:
#include <tuple>
int main ()
{
auto f = [] () -> decltype (auto)
{
return std::get<0> (std::make_tuple (0));
};
return f ();
}
(Silently) generates code with undefined behaviour - the temporary rvalue returned by make_tuple is propagated through the std::get<> and through the decltype(auto) onto the return type. So it ends up returning a reference to a temporary that has gone out of scope. See it here https://godbolt.org/g/X1UhSw.
Now, you could argue that my use of decltype(auto) is at fault. But in my generic code (where the type of the tuple might be std::tuple<Foo &>) I don't want to always make a copy. I really do want to extract the exact value or reference from the tuple.
My feeling is that this overload of std::get is dangerous:
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
get( tuple<Types...>&& t ) noexcept;
Whilst propagating lvalue references onto tuple elements is probably sensible, I don't think that holds for rvalue references.
I'm sure the standards committee thought this through very carefully, but can anyone explain to me why this was considered the best option?
Consider the following example:
void consume(foo&&);
template <typename Tuple>
void consume_tuple_first(Tuple&& t)
{
consume(std::get<0>(std::forward<Tuple>(t)));
}
int main()
{
consume_tuple_first(std::tuple{foo{}});
}
In this case, we know that std::tuple{foo{}} is a temporary and that it will live for the entire duration of the consume_tuple_first(std::tuple{foo{}}) expression.
We want to avoid any unnecessary copy and move, but still propagate the temporarity of foo{} to consume.
The only way of doing that is by having std::get return an rvalue reference when invoked with a temporary std::tuple instance.
live example on wandbox
Changing std::get<0>(std::forward<Tuple>(t)) to std::get<0>(t) produces a compilation error (as expected) (on wandbox).
Having a get alternative that returns by value results in an additional unnecessary move:
template <typename Tuple>
auto myget(Tuple&& t)
{
return std::get<0>(std::forward<Tuple>(t));
}
template <typename Tuple>
void consume_tuple_first(Tuple&& t)
{
consume(myget(std::forward<Tuple>(t)));
}
live example on wandbox
but can anyone explain to me why this was considered the best option?
Because it enables optional generic code that seamlessly propagates temporaries rvalue references when accessing tuples. The alternative of returning by value might result in unnecessary move operations.
IMHO this is dangerous and quite sad since it defeats the purpose of the "most important const":
Normally, a temporary object lasts only until the end of the full expression in which it appears. However, C++ deliberately specifies that binding a temporary object to a reference to const on the stack lengthens the lifetime of the temporary to the lifetime of the reference itself, and thus avoids what would otherwise be a common dangling-reference error.
In light of the quote above, for many years C++ programmers have learned that this was OK:
X const& x = f( /* ... */ );
Now, consider this code:
struct X {
void hello() const { puts("hello"); }
~X() { puts("~X"); }
};
auto make() {
return std::variant<X>{};
}
int main() {
auto const& x = std::get<X>(make()); // #1
x.hello();
}
I believe anyone should be forgiven for thinking that line #1 is OK. However, since std::get returns a reference to an object that is going to be destroyed, x is a dangling reference. The code above outputs:
~X
hello
which shows that the object that x binds to is destroyed before hello() is called. Clang gives a warning about the issue but gcc and msvc don't. The same issue happens if (as in the OP) we use std::tuple instead of std::variant but, sadly enough, clang doesn't issues a warning for this case.
A similar issue happens with std::optional and this value overload:
constexpr T&& value() &&;
This code, which uses the same X above, illustrates the issue:
auto make() {
return std::optional{X{}};
}
int main() {
auto const& x = make().value();
x.hello();
}
The output is:
~X
~X
hello
Brace yourself for more of the same with C++23's std::except and its methods value() and error():
constexpr T&& value() &&;
constexpr E&& error() && noexcept;
I'd rather pay the price of the move explained in Vittorio Romeo's post. Sure, I can avoid the issue by removing & from lines #1 and #2. My point is that the rule for the "most important const" just became more complicated and we need to consider if the expression involves std::get, std::optional::value, std::expected::value, std::expected::error, ...
Related
When using forwarding references, is it a bad idea to forward the
same value to more than one function? Consider the following piece of code:
template<typename Container>
constexpr auto
front(Container&& c)
-> typename Container::value_type
{ return std::forward<Container>(c).front(); }
template<typename Container>
constexpr auto
back(Container&& c)
-> typename Container::value_type
{ return std::forward<Container>(c).back(); }
template<typename Container>
constexpr auto
get_corner(Container&& c)
{
return do_something(front(std::forward<Container(c)),
back(std::forward<Container>(c));
}
If Container is an lvalue-reference, the function works just fine. However, I'm worrying about situations where rvalues are passed on to it, because the value would get invalidated once a move occurs. My doubt is: Is there a correct way to forward the container in that case, without losing the value category?
In general, it is not reasonable for the same function to forward the same parameter twice. Not unless it has specific knowledge of what the receiver of that forwarded parameter will do.
Remember: the behavior of std::forward can be equivalent to the behavior of std::move, depending on what parameter the user passed in. And the behavior of an xvalue will be contingent on how the receiving function processes it. If the receiver takes a non-const rvalue reference, it will likely move from that value if possible. That would leave you holding a moved-from object. If it takes a value, it will certainly move from it if the type supports it.
So unless you have specific knowledge of the expected behavior of the operations you are using, it is not safe to forward a parameter more than once.
There's actually no rvalue-reference version of std::begin - we just have (setting aside constexpr and return values):
template <class C>
??? begin(C& );
template <class C>
??? begin(C const& );
For lvalue containers, you get iterator, and for rvalue containers, you get const_iterator (or whatever the container-specific equivalent ends up being).
The one real problem in your code is returning decltype(auto). For lvalue containers, that's fine - you'll return a reference to an object whose lifetime exceeds the function. But for rvalue containers, that's returning a dangling reference. You'll want to return a reference for lvalue containers and a value for rvalue containers.
On top of that, forward-ing the containers into begin()/end() is probably not what you want to do. It'd be more efficient to conditionally wrap the result of the select() as a move iterator. Something like this answer of mine:
template <typename Container,
typename V = decltype(*std::begin(std::declval<Container&>())),
typename R = std::conditional_t<
std::is_lvalue_reference<Container>::value,
V,
std::remove_reference_t<V>
>
>
constexpr R operator()(Container&& c)
{
auto it = select(std::begin(c), std::end(c));
return *make_forward_iterator<Container>(it);
}
There's probably a less verbose way to express all of that.
You presumably realize that you wouldn't want to std::move an object being passed to multiple functions:
std::string s = "hello";
std::string hello1 = std::move(s);
std::string hello2 = std::move(s); // hello2 != "hello"
The role of forward is simply to restore any rvalue status that a parameter had when it was passed to the function.
We can quickly demonstrate that it is bad practice by forwarding one parameter two times to a function that has a move effect:
#include <iostream>
#include <string>
struct S {
std::string name_ = "defaulted";
S() = default;
S(const char* name) : name_(name) {}
S(S&& rhs) { std::swap(name_, rhs.name_); name_ += " moved"; }
};
void fn(S s)
{
std::cout << "fn(" << s.name_ << ")\n";
}
template<typename T>
void fwd_test(T&& t)
{
fn(std::forward<T>(t));
fn(std::forward<T>(t));
}
int main() {
fwd_test(S("source"));
}
http://ideone.com/NRM8Ph
If forwarding was safe, we should see fn(source moved) twice, but instead we see:
fn(source moved)
fn(defaulted moved)
In general, yes, this is potentially dangerous.
Forwarding a parameter ensures that if the value received by the universal reference parameter is an rvalue of some sort, it will continue to be an rvalue when it is forwarded. If the value is ultimately forwarded to a function (such as a move-constructor) that consumes the value by moving from it, its internal state is not likely to be valid for use in subsequent calls.
If you do not forward the parameter, it will not (in general) be eligible for move operations, so you would be safe from such behavior.
In your case, front and back (both the free functions and the member functions) do not perform a move on the container, so the specific example you gave should be safe. However, this also demonstrates that there's no reason to forward the container, since an rvalue won't be given different treatment from an lvalue--which is the only reason to preserve the distinction by forwarding the value in the first place.
How do I write a generic forwarding lambda in C++14?
Try #1
[](auto&& x) { return x; }
Inside the function body, x is an lvalue, so this doesn't work.
Try #2
[](auto&& x) { return std::forward<decltype(x)>(x); }
This properly forwards references inside the lambda, but it will always return by value (unless the compiler elides the copy).
Try #3
[](auto&& x) -> decltype(x) { return std::forward<decltype(x)>(x); }
This returns the same type as the argument (probably -> auto&& would work as well) and appears to work properly.
Try #4
[](auto&& x) noexcept -> decltype(x) { return std::forward<decltype(x)>(x); }
Does adding noexcept make this lambda more applicable and thus strictly better than #3?
A return type of decltype(x) is insufficient.
Local variables and function parameters taken by value can be implicitly moved into the return value, but not function parameters taken by rvalue reference (x is an lvalue, even though decltype(x) == rvalue reference if you pass an rvalue). The reasoning the committee gave is that they wanted to be certain that when the compiler implicitly moves, no one else could possibly have it. That is why we can move from a prvalue (a temporary, a non-reference qualified return value) and from function-local values. However, someone could do something silly like
std::string str = "Hello, world!";
f(std::move(str));
std::cout << str << '\n';
And the committee didn't want to silently invoke an unsafe move, figuring that they should start out more conservative with this new "move" feature. Note that in C++20, this issue will be resolved, and you can simply do return x and it will do the right thing. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0527r0.html
For a forwarding function, I would attempt to get the noexcept correct. It is easy here since we are just dealing with references (it is unconditionally noexcept). Otherwise, you break code that cares about noexcept.
This makes the final ideal forwarding lambda look as follows:
auto lambda = [](auto && x) noexcept -> auto && { return std::forward<decltype(x)>(x); };
A return type of decltype(auto) will do the same thing here, but auto && better documents that this function always returns a reference. It also avoids mentioning the variable name again, which I suppose makes it slightly easier to rename the variable.
As of C++17, the forwarding lambda is also implicitly constexpr where possible.
Your first two tries don't work because return type deduction drops top-level cv-qualifiers and references, which makes generic forwarding impossible. Your third is exactly correct just unnecessarily verbose, the cast is implicit in the return type. And noexcept is unrelated to forwarding.
Here are a few more options worth throwing out for completeness:
auto v0 = [](auto&& x) -> decltype(x) { return x; };
auto v1 = [](auto&& x) -> auto&& { return x; };
auto v2 = [](auto&& x) -> auto&& { return std::forward<decltype(x)>(x); };
auto v3 = [](auto&& x) -> decltype(auto) { return std::forward<decltype(x)>(x); };
v0 will compile and look as if it returns the correct type, but will fail at compilation if you call it with an rvalue reference due to the requested implicit conversion from lvalue reference (x) to rvalue reference (decltype(x)). This fails on both gcc and clang.
v1 will always return an lvalue reference. If you call it with temporary, that gives you a dangling reference.
Both v2 and v3 are correct, generic forwarding lambdas. Thus, you have three options for your trailing return type (decltype(auto), auto&&, or decltype(x)) and one option for the body of your lambda (a call to std::forward).
The fewest characters, yet fully featured, version I can generate is:
[](auto&&x)noexcept->auto&&{return decltype(x)(x);}
this uses an idiom I find useful -- when forwarding auto&& parameters in a lambda, do decltype(arg)(arg). Forwarding the decltype through forward is relatively pointless if you know that the arg is of reference type.
If x was of value type, decltype(x)(x) actually produces a copy of x, while std::forward<decltype(x)>(x) produces an rvalue reference to x. So the decltype(x)(x) pattern is less useful in the general case than forward: but this isn't the general case.
auto&& will allow references to be returned (matching the incoming references). Sadly, reference lifetime extension won't work properly with the above code -- I find that forwarding rvalue references into rvalue references is often the wrong solution.
template<class T>struct tag{using type=T;};
template<class Tag>using type=typename Tag::type;
template<class T> struct R_t:tag<T>{};
template<class T> struct R_t<T&>:tag<T&>{};
template<class T> struct R_t<T&&>:tag<T>{};
template<class T>using R=type<R_t<T>>;
[](auto&&x)noexcept->R<decltype(x)>{return decltype(x)(x);}
gives you that behavior. lvalue references become lvalue references, rvalue references become values.
C++11 (and C++14) introduces additional language constructs and improvements that target generic programming. These include features such as;
R-value references
Reference collapsing
Perfect forwarding
Move semantics, variadic templates and more
I was browsing an earlier draft of the C++14 specification (now with updated text) and the code in an example in ยง20.5.1, Compile-time integer sequences, that I found interesting and peculiar.
template<class F, class Tuple, std::size_t... I>
decltype(auto) apply_impl(F&& f, Tuple&& t, index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
}
template<class F, class Tuple>
decltype(auto) apply(F&& f, Tuple&& t) {
using Indices = make_index_sequence<std::tuple_size<Tuple>::value>;
return apply_impl(std::forward<F>(f), std::forward<Tuple>(t), Indices());
}
Online here [intseq.general]/2.
Question
Why was the function f in apply_impl being forwarded, i.e. why std::forward<F>(f)(std::get...?
Why not just apply the function as f(std::get...?
In Brief...
The TL;DR, you want to preserve the value category (r-value/l-value nature) of the functor because this can affect the overload resolution, in particular the ref-qualified members.
Function definition reduction
To focus on the issue of the function being forwarded, I've reduced the sample (and made it compile with a C++11 compiler) to;
template<class F, class... Args>
auto apply_impl(F&& func, Args&&... args) -> decltype(std::forward<F>(func)(std::forward<Args>(args)...)) {
return std::forward<F>(func)(std::forward<Args>(args)...);
}
And we create a second form, where we replace the std::forward(func) with just func;
template<class F, class... Args>
auto apply_impl_2(F&& func, Args&&... args) -> decltype(func(std::forward<Args>(args)...)) {
return func(std::forward<Args>(args)...);
}
Sample evaluation
Evaluating some empirical evidence of how this behaves (with conforming compilers) is a neat starting point for evaluating why the code example was written as such. Hence, in addition we will define a general functor;
struct Functor1 {
int operator()(int id) const
{
std::cout << "Functor1 ... " << id << std::endl;
return id;
}
};
Initial sample
Run some sample code;
int main()
{
Functor1 func1;
apply_impl_2(func1, 1);
apply_impl_2(Functor1(), 2);
apply_impl(func1, 3);
apply_impl(Functor1(), 4);
}
And the output is as expected, independent of whether an r-value is used Functor1() or an l-value func when making the call to apply_impl and apply_impl_2 the overloaded call operator is called. It is called for both r-values and l-values. Under C++03, this was all you got, you could not overload member methods based on the "r-value-ness" or "l-value-ness" of the object.
Functor1 ... 1
Functor1 ... 2
Functor1 ... 3
Functor1 ... 4
Ref-qualified samples
We now need to overload that call operator to stretch this a little further...
struct Functor2 {
int operator()(int id) const &
{
std::cout << "Functor2 &... " << id << std::endl;
return id;
}
int operator()(int id) &&
{
std::cout << "Functor2 &&... " << id << std::endl;
return id;
}
};
We run another sample set;
int main()
{
Functor2 func2;
apply_impl_2(func2, 5);
apply_impl_2(Functor2(), 6);
apply_impl(func2, 7);
apply_impl(Functor2(), 8);
}
And the output is;
Functor2 &... 5
Functor2 &... 6
Functor2 &... 7
Functor2 &&... 8
Discussion
In the case of apply_impl_2 (id 5 and 6), the output is not as may have been initially been expected. In both cases, the l-value qualified operator() is called (the r-value is not called at all). It may have been expected that since Functor2(), an r-value, is used to call apply_impl_2 the r-value qualified operator() would have been called. The func, as a named parameter to apply_impl_2, is an r-value reference, but since it is named, it is itself an l-value. Hence the l-value qualified operator()(int) const& is called in both the case of the l-value func2 being the argument and the r-value Functor2() being used as the argument.
In the case of apply_impl (id 7 and 8) the std::forward<F>(func) maintains or preserves the r-value/l-value nature of the argument provided for func. Hence the l-value qualified operator()(int) const& is called with the l-value func2 used as the argument and the r-value qualified operator()(int)&& when the r-value Functor2() is used as the argument. This behaviour is what would have been expected.
Conclusions
The use of std::forward, via perfect forwarding, ensures that we preserve the r-value/l-value nature of the original argument for func. It preserves their value category.
It is required, std::forward can and should be used for more than just forwarding arguments to functions, but also when the use of an argument is required where the r-value/l-value nature must be preserved. Note; there are situations where the r-value/l-value cannot or should not be preserved, in these situations std::forward should not be used (see the converse below).
There are many examples popping up that inadvertently lose the r-value/l-value nature of the arguments via a seemingly innocent use of an r-value reference.
It has always been hard to write well defined and sound generic code. With the introduction of r-value references, and reference collapsing in particular, it has become possible to write better generic code, more concisely, but we need to be ever more aware of what the original nature of the arguments provided are and make sure that they are maintained when we use them in the generic code we write.
Full sample code can be found here
Corollary and converse
A corollary of the question would be; given reference collapsing in a templated function, how is the r-value/l-value nature of the argument maintained? The answer - use std::forward<T>(t).
Converse; does std::forward solve all your "universal reference" problems? No it doesn't, there are cases where it should not be used, such as forwarding the value more than once.
Brief background to perfect forwarding
Perfect forwarding may be unfamiliar to some, so what is perfect forwarding?
In brief, perfect forwarding is there to ensure that the argument provided to a function is forwarded (passed) to another function with the same value category (basically r-value vs. l-value) as originally provided. It is typically used with template functions where reference collapsing may have taken place.
Scott Meyers gives the following pseudo code in his Going Native 2013 presentation to explain the workings of std::forward (at approximately the 20 minute mark);
template <typename T>
T&& forward(T&& param) { // T&& here is formulated to disallow type deduction
if (is_lvalue_reference<T>::value) {
return param; // return type T&& collapses to T& in this case
}
else {
return move(param);
}
}
Perfect forwarding depends on a handful of fundamental language constructs new to C++11 that form the bases for much of what we now see in generic programming:
Reference collapsing
Rvalue references
Move semantics
The use of std::forward is currently intended in the formulaic std::forward<T>, understanding how std::forward works helps understand why this is such, and also aids in identifying non-idiomatic or incorrect use of rvalues, reference collapsing and ilk.
Thomas Becker provides a nice, but dense write up on the perfect forwarding problem and solution.
What are ref-qualifiers?
The ref-qualifiers (lvalue ref-qualifier & and rvalue ref-qualifier &&) are similar to the cv-qualifiers in that they (the ref-qualified members) are used during overload resolution to determine which method to call. They behave as you would expect them to; the & applies to lvalues and && to rvalues. Note: Unlike cv-qualification, *this remains an l-value expression.
Here is a practical example.
struct concat {
std::vector<int> state;
std::vector<int> const& operator()(int x)&{
state.push_back(x);
return state;
}
std::vector<int> operator()(int x)&&{
state.push_back(x);
return std::move(state);
}
std::vector<int> const& operator()()&{ return state; }
std::vector<int> operator()()&&{ return std::move(state); }
};
This function object takes an x, and concatenates it to an internal std::vector. It then returns that std::vector.
If evaluated in an rvalue context it moves to a temporary, otherwise it returns a const& to the internal vector.
Now we call apply:
auto result = apply( concat{}, std::make_tuple(2) );
because we carefully forwarded our function object, only 1 std::vector buffer is allocated. It is simply moved out to result.
Without the careful forwarding, we end up creating an internal std::vector, and we copy it to result, then discard the internal std::vector.
Because the operator()&& knows that the function object should be treated as a rvalue about to be destroyed, it can rip the guts out of the function object while doing its operation. The operator()& cannot do this.
Careful use of perfect forwarding of function objects enables this optimization.
Note, however, that there is very little use of this technique "in the wild" at this point. Rvalue qualified overloading is obscure, and doing so to operator() moreso.
I could easily see future versions of C++ automatically using the rvalue state of a lambda to implicitly move its captured-by-value data in certain contexts, however.
boost::reference_wrapper<T> has an explicit T& constructor, while std::reference_wrapper<T> has an implicit one. So, in the following code:
foo = bar;
If foo is a boost::reference_wrapper, the code will fail to compile (which is good, since reference_wrapper does not have the same semantics of an actual reference.
If foo is a std::reference_wrapper, the code will "rebind" foo's reference to bar (instead of assigning the value as one might mistakenly expect it to).
This could result in elusive bugs... Consider the following example:
In version 1.0 of some hypothetical library:
void set_max(int& i, int a, int b) {
i = (a > b) ? a : b;
}
In a new version (1.1), set_max is converted to a template to accept integers of any width (or UDT's) without changing the interface:
template<typename T1, typename T2, typename T3>
void set_max(T1& i, T2 a, T3 b) {
i = (a > b) ? a : b;
}
Then finally, in some application using the library:
// i is a std::reference_wrapper<int> passed to this template function or class
set_max(i, 7, 11);
In this example, the library changes its implementation of set_max without changing the call interface. This would silently break any code that passes it a std::reference_wrapper as the argument would no longer convert to int& and would instead "rebind" to a dangling reference (a or b).
My question: Why did the standards committee elect to allow implicit conversion (from T& to std::reference_wrapper<T>) instead of following boost and making the T& constructor explicit?
Edit: (in response to the answer offered by Jonathan Wakely)...
The original demo (in the section above) is intentionally concise to show how a subtle library change could result in the use of std::reference_wrapper introducing bugs to an application.
The next demo is provided to show a real-world, legitimate use of reference_wrapper for "passing references through interfaces", in response to Jonathan Wakely's point.
From Developer/Vendor A
Something similar to std::bind but pretend it's specialized for some task:
template<typename FuncType, typename ArgType>
struct MyDeferredFunctionCall
{
MyDeferredFunctionCall(FuncType _f, ArgType _a) : f(_f), a(_a) {}
template<typename T>
void operator()(T t) { f(a, t); }
FuncType f;
ArgType a;
};
From Developer/Vendor B
A RunningMax functor class. Between version 1.0 and 1.1 of this imaginary library, the implementation of RunningMax was changed to be more generic, without changing its call interface. For purposes of this demo, the old implementation is defined in namespace lib_v1, while the new implementation in defined in lib_v2:
namespace lib_v1 {
struct RunningMax {
void operator()(int& curMax, int newVal) {
if ( newVal > curMax ) { curMax = newVal; }
}
};
}
namespace lib_v2 {
struct RunningMax {
template<typename T1, typename T2>
void operator()(T1& curMax, T2 newVal) {
if ( newVal > curMax ) { curMax = newVal; }
}
};
}
And last but not least, the end-user of all the above code:
Some developer using the code from Vendor/Developer A and B to accomplish some task:
int main() {
int _i = 7;
auto i = std::ref(_i);
auto f = lib_v2::RunningMax{};
using MyDFC = MyDeferredFunctionCall<decltype(f), decltype(i)>;
MyDFC dfc = MyDFC(f, i);
dfc(11);
std::cout << "i=[" << _i << "]" << std::endl; // should be 11
}
Note the following:
The end-user uses std::reference_wrapper the way in which it's intended.
Individually, none of the code has bugs or logical flaws, and everything worked perfectly with the original version of Vendor B's library.
boost::reference_wrapper would fail to compile upon upgrading the library, while std::reference_wrapper silently introduces a bug that may or may not be caught in regression tests.
Tracing such a bug would be difficult, since the "rebinding" is not a memory-error that tools such as valgrind would catch. Furthermore, the actual site of the misuse of std::reference_wrapper would be within Vendor B's library code, not the end-user's.
The bottom line: boost::reference_wrapper seems safer by declaring its T& constructor as explicit, and would prevent the introduction of a bug such as this. The decision to remove explicit constructor restriction in std::reference_wrapper seems like it compromised safety for convenience, something that should rarely occur in language/library design.
This would silently break any code that passes it a std::reference_wrapper as the argument would no longer convert to int& and would instead "rebind" to a dangling reference (a or b).
So don't do that.
reference_wrapper is for passing references through interfaces that would otherwise make a by-value copy, it's not for passing to arbitrary code.
Also:
// i is a std::reference_wrapper<int> (perhaps b/c std::decay wasn't used)
decay wouldn't change anything, it doesn't affect reference wrappers.
The reason implicit conversion (T& --> reference_wrapper<T>) is allowed for std::reference_wrapper<T>, but not boost::reference_wrapper<T>, is sufficiently explained in the DR-689 link provided by Nate Kohl. To summarize:
In 2007, the C++0x/C++11 Library Working Group (LWG) proposed change #DR-689 to section 20.8.3.1 [refwrap.const] of the standard:
The constructor of reference_wrapper is currently explicit.
The primary motivation behind this is the safety problem with respect
to rvalues, which is addressed by the proposed resolution of [DR-688].
Therefore we should consider relaxing the requirements
on the constructor since requests for the implicit conversion keep
resurfacing.
Proposed resolution: Remove explicit from the constructor of reference_wrapper.
It's worth pointing out:
boost::reference_wrapper has not been relaxed in such a way, nor does there appear to be a proposal for it, which creates an inconsistency between the semantics of boost::reference_wrapper and std::reference_wrapper.
Based on the verbiage in DR-689 (specifically the "requests keep surfacing" part) it seems likely that this change was simply viewed by the LWG as an acceptable tradeoff between safety and convenience (in contrast to its boost counterpart).
It's unclear whether the LWG anticipated other potential risks (such as those demonstrated in the examples provided on this page), since the only risk mentioned in DR-689 was that of binding to an rvalue (as described and resolved in the previous entry, DR-688).
I've been watching Scott Meyers' talk on Universal References from the C++ and Beyond 2012 conference, and everything makes sense so far. However, an audience member asks a question at around 50 minutes in that I was also wondering about. Meyers says that he does not care about the answer because it is non-idiomatic and would silly his mind, but I'm still interested.
The code presented is as follows:
// Typical function bodies with overloading:
void doWork(const Widget& param) // copy
{
// ops and exprs using param
}
void doWork(Widget&& param) // move
{
// ops and exprs using std::move(param)
}
// Typical function implementations with universal reference:
template <typename T>
void doWork(T&& param) // forward => copy and move
{
// ops and exprs using std::forward<T>(param)
}
The point being that when we take an rvalue reference, we know we have an rvalue, so we should std::move it to preserve the fact that it's an rvalue. When we take a universal reference (T&&, where T is a deduced type), we want std::forward to preserve the fact that it may have been an lvalue or an rvalue.
So the question is: since std::forward preserves whether the value passed into the function was either an lvalue or an rvalue, and std::move simply casts its argument to an rvalue, could we just use std::forward everywhere? Would std::forward behave like std::move in all cases where we would use std::move, or are there some important differences in behaviour that are missed out by Meyers' generalisation?
I'm not suggesting that anybody should do it because, as Meyers correctly says, it's completely non-idiomatic, but is the following also a valid use of std::move:
void doWork(Widget&& param) // move
{
// ops and exprs using std::forward<Widget>(param)
}
The two are very different and complementary tools.
std::move deduces the argument and unconditionally creates an rvalue expression. This makes sense to apply to an actual object or variable.
std::forward takes a mandatory template argument (you must specify this!) and magically creates an lvalue or an rvalue expression depending on what the type was (by virtue of adding && and the collapsing rules). This only makes sense to apply to a deduced, templated function argument.
Maybe the following examples illustrate this a bit better:
#include <utility>
#include <memory>
#include <vector>
#include "foo.hpp"
std::vector<std::unique_ptr<Foo>> v;
template <typename T, typename ...Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); // #1
}
int main()
{
{
std::unique_ptr<Foo> p(new Foo('a', true, Bar(1,2,3)));
v.push_back(std::move(p)); // #2
}
{
v.push_back(make_unique<Foo>('b', false, Bar(5,6,7))); // #3
}
{
Bar b(4,5,6);
char c = 'x';
v.push_back(make_unique<Foo>(c, b.ready(), b)); // #4
}
}
In situation #2, we have an existing, concrete object p, and we want to move from it, unconditionally. Only std::move makes sense. There's nothing to "forward" here. We have a named variable and we want to move from it.
On the other hand, situation #1 accepts a list of any sort of arguments, and each argument needs to be forwarded as the same value category as it was in the original call. For example, in #3 the arguments are temporary expressions, and thus they will be forwarded as rvalues. But we could also have mixed in named objects in the constructor call, as in situation #4, and then we need forwarding as lvalues.
Yes, if param is a Widget&&, then the following three expressions are equivalent (assuming that Widget is not a reference type):
std::move(param)
std::forward<Widget>(param)
static_cast<Widget&&>(param)
In general (when Widget may be a reference), std::move(param) is equivalent to both of the following expressions:
std::forward<std::remove_reference<Widget>::type>(param)
static_cast<std::remove_reference<Widget>::type&&>(param)
Note how much nicer std::move is for moving stuff. The point of std::forward is that it mixes well with template type deduction rules:
template<typename T>
void foo(T&& t) {
std::forward<T>(t);
std::move(t);
}
int main() {
int a{};
int const b{};
//Deduced T Signature Result of `forward<T>` Result of `move`
foo(a); //int& foo(int&) lvalue int xvalue int
foo(b); //int const& foo(int const&) lvalue int const xvalue int const
foo(int{});//int foo(int&&) xvalue int xvalue int
}