As I understand rvalue being passed as an argument into function becomes lvalue,
std::forward returns rvalue if argument was passed as rvalue and lvalue if it was passed as lvalue. Here is my class:
#include <string>
#include <iostream>
struct MyClass
{
MyClass()
{
std::cout << "default";
}
MyClass(const MyClass& copy)
{
std::cout << "copy";
}
MyClass& operator= (const MyClass& right)
{
std::cout << "=";
return *this;
}
MyClass& operator= (const MyClass&& right)
{
std::cout << "mov =";
return *this;
}
MyClass(MyClass&& mov)
{
std::cout << "mov constructor";
}
};
void foo(MyClass s)
{
MyClass z = MyClass(std::forward<MyClass>(s));
}
void main()
{
auto a = MyClass();
foo(MyClass()); //z is created by move_constructor
foo(a); //z is created by move_constructor, but I think it must be created using copy constructor
}
My question is: why z variable is created using move_constructor in both cases.
I thought it must be moved in first case foo(MyClass()) and copied in 2nd case foo(a). In second case I pass lvalue as argument s, and std::forward must return lvalue, that is then is passed as lvalue reference into MyClass constructor. Where am I wrong?
I think you are sufficiently confused. The role of forward is only important when universal references come into play, and universal reference is something like T&& t but only when T is a template parameter.
For example, in void foo(X&& x); x is not a forwarding reference, it is a normal rvalue reference, and forwarding it makes no sense. Rather, you use std::move if you want to preserve it's rvalueness, otherwise it becomes an l-value:
void foo(X&& x) {
bar(x); // calls bar with an l-value x, x should be not moved from
baz(std::move(x)); // calls bar with an r-value x, x is likely moved from after this and probably unusable
}
In other words, above function foo was specifically crafted to take rvalue references as it's argument, and will not accept anything else. You, as a function writer, defined it's contract in such way.
In contrast, in a context like template <class T> void foo(T&& t) t is a forwarding reference. Due to reference collapsing rule, it could be an rvalue or an lvalue reference, depending on the valueness of the expression given to the function foo at the call site. In such case, you use
template<class T>
void foo(T&& t) {
// bar is called with value matching the one at the call site
bar(std::forward<T>(t));
}
The type of the argument that you've declared is MyClass. Whatever is the expression that initialises the argument is irrelevant in the case of your function - it does not affect the type of the argument.
MyClass is not a reference type. std::forward converts an lvalue expression of non-reference type to an rvalue. The use of std::forward in this context is equivalent to std::move.
Note that the argument itself is copy-constructed in the call foo(a).
Related
I thought to myself that I don't need std::forward<T>(arg); in my function because I wasn't passing the argument on to another function, I was using it directly. However then I thought even if I use it directly, by for example assigning it, or using it as a constructor argument then those each are function calls, respectively to operator= and constructor, which is a function call:
template <typename element_T>
void push_back(element_t&& copy)
{
*_end = copy; // This calls operator=, which is a function
}
template <typename ... ConstructorArgs>
void emplace_back(ConstructorArgs&& ... args)
{
new (_end) element_t(args);
// Calls constructor, needs (std::forward<ConstructorArgs>(args)...) ?
}
Do I need the calls to std::forward in these cases?
An expression that is a name of a variable is always an lvalue. For example, in
1 template <typename T>
2 void push_back(T&& copy) {
3 *_end = copy;
4 }
copy in line 3 has the lvalue value category no matter what type is deduced for T. Depending on how operator= in line 3 is defined/overloaded, this might result in selecting a wrong overload or in wrong type deduction.
Consider the following simple example (that follows the Ted Lyngmo's example from the comments section):
struct A {
void operator=(const A&); // (1)
void operator=(A&&); // (2)
};
struct C {
template<class T>
void push_back(T&& copy) {
a = copy;
}
A a;
};
C{}.push_back(A{});
Which A's assignment operator will be invoked here? One might expect (2) because A{} is a prvalue, but the correct answer is (1), because copy is an lvalue, and lvalues can't bind to rvalue references.
If we change the assignment to
a = std::forward<T>(copy);
then the expression std::forward<T>(copy) will have the xvalue value category and the (2) assignment operator will be called, as expected.
Placement new follows the same reasoning.
I can't remember which talk it was, but recently I watched some talks from CppCon 2017 and there someone mentioned as some kind of side-note, that the only true way of overloading operator= would be in the following fashion:
class test {
public:
test& operator=(const test&) &;
};
He explicitly emphasized the trailing & but didn't say what it does.
So what does it do?
Ref-qualifiers - introduced in C++11
Ref-qualifiers is not C++17 feature (looking at the tag of the question), but was a feature introduced in C++11.
struct Foo
{
void bar() const & { std::cout << "const lvalue Foo\n"; }
void bar() & { std::cout << "lvalue Foo\n"; }
void bar() const && { std::cout << "const rvalue Foo\n"; }
void bar() && { std::cout << "rvalue Foo\n"; }
};
const Foo&& getFoo() { return std::move(Foo()); }
int main()
{
const Foo c_foo;
Foo foo;
c_foo.bar(); // const lvalue Foo
foo.bar(); // lvalue Foo
getFoo().bar(); // [prvalue] const rvalue Foo
Foo().bar(); // [prvalue] rvalue Foo
// xvalues bind to rvalue references, and overload resolution
// favours selecting the rvalue ref-qualifier overloads.
std::move(c_foo).bar(); // [xvalue] const rvalue Foo
std::move(foo).bar(); // [xvalue] rvalue Foo
}
Note that an rvalue may be used to initialize a const lvalue reference (and in so expanding the lifetime of the object identified by the rvalue), meaning that if we remove the rvalue ref-qualifier overloads from the example above, then the rvalue value categories in the example will all favour the remaining const & overload:
struct Foo
{
void bar() const & { std::cout << "const lvalue Foo\n"; }
void bar() & { std::cout << "lvalue Foo\n"; }
};
const Foo&& getFoo() { return std::move(Foo()); }
int main()
{
const Foo c_foo;
Foo foo;
// For all rvalue value categories overload resolution
// now selects the 'const &' overload, as an rvalue may
// be used to initialize a const lvalue reference.
c_foo.bar(); // const lvalue Foo
foo.bar(); // lvalue Foo
getFoo().bar(); // const lvalue Foo
Foo().bar(); // const lvalue Foo
std::move(c_foo).bar(); // const lvalue Foo
std::move(foo).bar(); // const lvalue Foo
}
See e.g. the following blog post for for a brief introduction:
Andrzej's C++ blog - Ref-qualifiers
rvalues cannot invoke non-const & overloads
To possibly explain the intent of your recollected quote from the CppCon talk,
"... that the only true way of overloading operator= ..."
we visit [over.match.funcs]/1, /4 & /5 [emphasis mine]:
/1 The subclauses of [over.match.funcs] describe the set of candidate functions and the argument list submitted to overload
resolution in each context in which overload resolution is used. ...
/4 For non-static member functions, the type of the implicit object parameter is
(4.1) — “lvalue reference to cv X” for functions declared without a ref-qualifier or with the & ref-qualifier
(4.2) — “rvalue reference to cv X” for functions declared with the && ref-qualifier
where X is the class of which the function is a member and cv is the
cv-qualification on the member function declaration. ...
/5 ... For non-static member functions declared without a ref-qualifier, an additional rule applies:
(5.1) — even if the implicit object parameter is not const-qualified, an rvalue can be bound to the parameter as long as
in all other respects the argument can be converted to the type of the
implicit object parameter. [ Note: The fact that such an argument is
an rvalue does not affect the ranking of implicit conversion
sequences. — end note ]
From /5 above, the following overload (where the explicit & ref-qualifier has been omitted)
struct test
{
test& operator=(const test&) { return *this }
}
allows assigning values to r-values, e.g.
int main()
{
test t1;
t1 = test(); // assign to l-value
test() = t1; // assign to r-value
}
However, if we explicitly declare the overload with the & ref-qualifier, [over.match.funcs]/5.1 does not apply, and as long we do not supply an overload declared with the && ref-qualifier, r-value assignment will not be allowed.
struct test
{
test& operator=(const test&) & { return *this; }
};
int main()
{
test t1;
t1 = test(); // assign to l-value
test() = t1; // error [clang]: error: no viable overloaded '='
}
I won't place any opinion as to whether explicitly including the & ref-qualifier when declaring custom assignment operator overloads is "the only true way of overload operator=", but would I dare to speculate, then I would guess that the intent behind such a statement is the exclusion of to-r-value assignment.
As a properly designed assignment operator should arguably never be const (const T& operator=(const T&) const & would not make much sense), and as an rvalue may not be used to initialize a non-const lvalue reference, a set of overloads for operator= for a given type T that contain only T& operator=(const T&) & will never proviade a viable overload that can be invoked from a T object identified to be of an rvalue value category.
As per http://en.cppreference.com/w/cpp/language/member_functions
the & following your member function declaration is lvalue ref-qualifier.
In other words, it requires this to be an l-value (the implicit object parameter has type lvalue reference to cv-qualified X).
There is also &&, which requires this to be an r-value.
To copy from documentation (const-, volatile-, and ref-qualified member functions):
#include <iostream>
struct S {
void f() & { std::cout << "lvalue\n"; }
void f() &&{ std::cout << "rvalue\n"; }
};
int main(){
S s;
s.f(); // prints "lvalue"
std::move(s).f(); // prints "rvalue"
S().f(); // prints "rvalue"
}
I followed this tutorial to start to understand the move semantics and rvalue references in C++11.
At some point, he implements these two classes with the std::move in the move constructors explaining that
we pass the temporary to a move constructor, and it takes on new life
in the new scope. In the context where the rvalue expression was
evaluated, the temporary object really is over and done with. But in
our constructor, the object has a name; it will be alive for the
entire duration of our function. In other words, we might use the
variable other more than once in the function, and the temporary
object has a defined location that truly persists for the entire
function. It's an lvalue in the true sense of the term locator value
class MetaData
{
public:
MetaData(int size, const string& name)
: _name(name)
, _size(size)
{}
MetaData(const MetaData& other)
: _name(other._name)
, _size(other._size)
{
cout << "MetaData -- Copy Constructor" << endl;
}
MetaData(MetaData&& other)
: _name(move(other._name))
, _size(other._size)
{
cout << "MetaData -- Move Constructor" << endl;
}
~MetaData()
{
_name.clear();
}
string getName() const { return _name; }
int getSize() const { return _size; }
private:
string _name;
int _size;
};
class ArrayWrapper
{
public:
ArrayWrapper()
: _p_vals(new int[64])
, _metadata(64, "ArrayWrapper")
{}
ArrayWrapper(int n)
: _p_vals(new int[n])
, _metadata(n, "ArrayWrapper")
{}
ArrayWrapper(ArrayWrapper&& other)
: _p_vals(other._p_vals)
, _metadata(move(other._metadata))
{
cout << "ArrayWrapper -- Move Constructor" << endl;
other._p_vals = nullptr;
}
ArrayWrapper(const ArrayWrapper& other)
: _p_vals(new int[other._metadata.getSize()])
, _metadata(other._metadata)
{
cout << "ArrayWrapper -- Copy Constructor" << endl;
for (int i = 0; i < _metadata.getSize(); ++i)
_p_vals[i] = other._p_vals[i];
}
~ArrayWrapper()
{
delete[] _p_vals;
}
int* getVals() const { return _p_vals; }
MetaData getMeta() const { return _metadata; }
private:
int* _p_vals;
MetaData _metadata;
};
In the ArrayWrapper move constructor I tried to change std::move with std::forward<MetaData> and the code shows that if I call the ArrayWrapper move constructor this will call the MetaData move constructor, like the example with the std::move.
Of course if I don't use either std::move or std::forward the MetaData copy costructor will be called.
The question is, in this case, is there a difference between using std::move and std::forward? Why should I use one instead of the other?
is there a difference between using std::move and std::forward? Why should I use one instead of the other?
Yes, std::move returns an rvalue reference of its parameter, while std::forward just forwards the parameter preserving its value category.
Use move when you clearly want to convert something to an rvalue. Use forward when you don't know what you've (may be an lvalue or an rvalue) and want to perfectly forward it (preserving its l or r valueness) to something. Can I typically/always use std::forward instead of std::move? is a question you might be interested in here.
In the below snippet, bar would get exactly what the caller of foo had passed, including its value category preserved:
template <class T>
void foo(T&& t) {
bar(std::forward<T>(t));
}
Don't let T&& fool you here - t is not an rvalue reference. When it appears in a type-deducing context, T&& acquires a special meaning. When foo is instantiated, T depends on whether the argument passed is an lvalue or an rvalue. If it's an lvalue of type U, T is deduced to U&. If it's an rvalue, T is deduced to U. See this excellent article for details. You need to understand about value categories and reference collapsing to understand things better in this front.
The relevant std::forward and std::move declarations are:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
template< class T >
typename std::remove_reference<T>::type&& move( T&& t );
For the former:
std::forward<MetaData>(other._metadata);
std::forward<MetaData> returns MetaData&&.
For the latter:
std::move(other._metadata);
//argument derived as lvalue reference due to forwarding reference
std::move<MetaData&>(other._name);
std::move<MetaData&> returns typename std::remove_reference<MetaData&>::type&&, which is MetaData&&.
So the two forms are identical for your example. However, std::move is the right choice here, as it shows our intent to unconditionally move the argument. std::forward can be used to unconditionally move, but the purpose of it is to perfect-forward its argument.
I have the following code directly from :
http://www.justsoftwaresolutions.co.uk/cplusplus/rvalue_references_and_perfect_forwarding.html
compiled at g++ 4.8.1 as : g++ -std=c++11 testforward.cpp -o testforward.exe
#include <cstdlib>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
class X
{
std::vector<double> data;
public:
X():
data(100000) // lots of data
{}
X(X const& other): // copy constructor
data(other.data) // duplicate all that data
{}
X(X&& other): // move constructor
data(std::move(other.data)) // move the data: no copies
{}
X& operator=(X const& other) // copy-assignment
{
data=other.data; // copy all the data
return *this;
}
X& operator=(X && other) // move-assignment
{
data=std::move(other.data); // move the data: no copies
return *this;
}
};
void g(X&& t)
{
std::cout << "t in g is rvalue" << std::endl ;
}
void g(X& t)
{
std::cout << "t in g is lvalue" << std::endl ;
}
template<typename T>
void f(T&&t)
{
g(std::forward<T>(t)) ;
}
void h(X &&t)
{
g(t) ;
}
int main()
{
X x;
f(x); // 1
f(X()); // 2
//h(x); //compile error
h(X()); // 3
}
According to the author describe below :
When you combine rvalue references with function templates you get an interesting interaction: if the type of a function parameter is an rvalue reference to a template type parameter then the type parameter is deduce to be an lvalue reference if an lvalue is passed, and a plain type otherwise...
The results output of this test are :
t in g is lvalue
t in g is rvalue
t in g is lvalue
f(x) get "t in g is lvalue" is just like expected !!
f(X()) get "t in g is rvalue" , yes,that is what std::forward used for
h(X()) get "t in g is lvalue" , this is my question ,as you can see that function h
is not a template function , as the author describe "When you combine rvalue references with function templates you get an interesting interaction" is not the case , still
this function output "t in g is lvalue" , means this interesting interaction happen not just in template function , also to normal function , too !!
if I change code to :
void h(X &&t)
{
g(std::forward<X>(t)) ;
}
I will got "t in g is rvalue" !!!
Accorind to test , May I said that the author describe "When you combine rvalue references with function templates you get an interesting interaction" actually not only to template function , it also apply to normal function , or my english is not good , so I can not catch this description right ?!
Edit :
void h(X &&t)
{
g(t) ;
}
void h(X &t)
{
g(t) ;
}
h(x); //get "t in g is lvalue"
h(X()); //get "t in g is lvalue"
=====================================================
void h(X &&t)
{
g(std::forward<X>(t)) ;
}
void h(X &t)
{
g(std::forward<X>(t)) ;
}
h(x); //get "t in g is rvalue"
h(X()); //get "t in g is rvalue"
Look like only in template function , I will get the cprrect usage of std::forward !!!
In h(X &&), the type of t is an r-value reference to X, but named variables are always treated as l-values. So, even though t is an X &&, t can not bind directly to an X && parameter, but only an X & parameter. This is for safety, since a named variable can (and often will) be used over and over again. You don't want the first usage of a variable to pilfer it, even if it did originally bind to an r-value. Subsequent uses would see the pilfered value, which will very easily lead to broken logic in code.
If you know a variable is an r-value (or more to the point, if you know you're done with it, whether it's an l-value or an r-value), the way to pass it on as an r-value is by using move(). The purpose of forward<T>() is for generic code, when you don't know whether the original value should be pilfered or not. If you were to use move() in a template, you may accidentally pilfer an l-value. So you use forward<T>() instead, which will resolve to a harmless pass-through if T is an l-value type, and will essentially become equivalent to move() if T is a non-reference or an r-value reference.
Note that in your edit, your second overload of h (that is, h(X &t)) is using forward<> incorrectly. The type of t in that situation is X &, so you should be using forward<X&>(t). If you did that, you would find that t is passed on as an l-value. However, in your two overloads of h, you can see that in the first, you have an r-value reference, and in the second you have an l-value reference. (There's no template deduction involved, so you know the types.) Therefore, you might as well use move() directly in your first overload, and not use anything in your second. The purpose of forward<T>() is to harvest information from the template deduction to determine whether it was bound to (and deduced as) an l-value or an r-value.
In the lecture about universal references, Scott Meyers (at approximately 40th minute) said that objects that are universal references should be converted into real type, before used. In other words, whenever there is a template function with universal reference type, std::forward should be used before operators and expressions are used, otherwise a copy of the object might be made.
My understanding of this is in the following example :
#include <iostream>
struct A
{
A() { std::cout<<"constr"<<std::endl; }
A(const A&) { std::cout<<"copy constr"<<std::endl; }
A(A&&) { std::cout<<"move constr"<<std::endl; }
A& operator=(const A&) { std::cout<<"copy assign"<<std::endl; return *this; }
A& operator=(A&&) { std::cout<<"move assign"<<std::endl; return *this; }
~A() { std::cout<<"destr"<<std::endl; }
void bar()
{
std::cout<<"bar"<<std::endl;
}
};
A getA()
{
A a;
return a;
}
template< typename T >
void callBar( T && a )
{
std::forward< T >( a ).bar();
}
int main()
{
{
std::cout<<"\n1"<<std::endl;
A a;
callBar( a );
}
{
std::cout<<"\n2"<<std::endl;
callBar( getA() );
}
}
As expected, the output is :
1
constr
bar
destr
2
constr
move constr
destr
bar
destr
The question really is why is this needed?
std::forward< T >( a ).bar();
I tried without std::forward, and it seems to work fine (the output is the same).
Similarly, why he recommends to use move inside the function with rvalue? (the answer is the same as for std::forward)
void callBar( A && a )
{
std::move(a).bar();
}
I understand that both std::move and std::forward are just casts to appropriate types, but are these casts really needed in the above example?
Bonus : how can the example be modified to produce the copy of the object that is passed to that function?
It's needed because bar() might be overloaded separately for rvalues and lvalues. That means that it might do something differently, or flat out not be allowed, depending on if you correctly described a as an lvalue or an rvalue, or just blindly treated it like an lvalue. Right now, most users don't use this functionality and don't have exposure to it because the most popular compilers don't support it - even GCC 4.8 doesn't support rvalue *this. But it is Standard.
There are two different uses for && on a parameter to a function. For an ordinary function it means that the argument is an rvalue reference; for a template function it means that it can be either an rvalue reference or an lvalue reference:
template <class T> void f(T&&); // rvalue or lvalue
void g(T&&); // rvalue only
void g(T&) // lvalue only
void h() {
C c;
f(c); // okay: calls f(T&)
f(std::move(c)); // okay: calls f(T&&)
g(c); // error: c is not an rvalue
g(std::move(c)); // okay: move turns c into an rvalue
}
Inside f and g, applying std::forward to such an argument preserves the lvalue- or rvalue-ness of the argument, so in general that's the safest way to forward an argument to another function.
void callBar( A && a )
{
std::move(a).bar();
}
In the case where you have an rvalue reference as a parameter, which can only bind to an rvalue you normally want to use move semantics to move from this this rvalue and take it's guts out.
The parameter itself is an lvalue, because it is a named thing. You can take it's address.
So in order to make it an rvalue again and be able to move from it, you apply std::move to it. If you were literally just calling a function on a passed parameter, I don't see why you'd have a parameter that is an rvalue reference.
You only want to pass an rvalue reference if you are going to move from this inside your function, which is why you then have to use std::move.
Your example here doesn't actually make much sense in that respect.
What is said in the lecture is this :
void doWork( Widget&& param )
{
ops and exprs using std::move(param)
}
SM: What this means is : if you see code that takes a rvalue reference, and you see use of that parameter without being wrapped by move, it is highly suspect.
After some thought, I realized that it is correct (as expected). Changing the callBar function in the original example to this demonstrate the point :
void reallyCallBar( A& la )
{
std::cout<<"lvalue"<<std::endl;
la.bar();
}
void reallyCallBar( A&& ra )
{
std::cout<<"rvalue"<<std::endl;
ra.bar();
}
template< typename T >
void callBar( T && a )
{
reallyCallBar( std::forward< T >( a ) );
}
If the std::forward wasn't used in callBar, then the reallyCallBar( A& ) would be used. Because a in callBar is a lvalue reference. std::forward makes it a rvalue, when the universal reference is the rvalue reference.
Next modification proves the point even further :
void reallyCallBar( A& la )
{
std::cout<<"lvalue"<<std::endl;
la.bar();
}
void reallyCallBar( A&& ra )
{
std::cout<<"rvalue"<<std::endl;
reallyCallBar( ra );
}
template< typename T >
void callBar( T && a )
{
reallyCallBar( std::forward< T >( a ) );
}
Since std::move is not used in the reallyCallBar( A&& ra ) function, it doesn't enter the endless loop. Instead it calls the version taking lvalue reference.
Therefore (as explained in the lecture) :
std::forward must be used on universal references
std::move must be used on rvalue references