I met this code and i dont know what is this, can someone explain to me?
template<class T> base{
protected:
T data;
public:
...
T&& unwrap() && { return std::move(data); }
operator T&&() && { return std::move(data); }
}
I know operator T&&() is a cast operator but i cant figure out what is the meaning of the bold && in:
operator T&&() &&
or
T&& unwrap() &&
The && and & qualifiers on a member function like that indicates the following:
The & qualified function will be called on lvalue instances of the object
The && qualified function will be called on rvalue instances of the object
Example:
#include <iostream>
class Example
{
private:
int whatever = 0;
public:
Example() = default;
// Lvalue ref qualifier
void getWhatever() & { std::cout << "This is called on lvalue instances of Example\n"; }
// Rvalue ref qualifier
void getWhatever() && { std::cout << "This is called on rvalue instances of Example\n"; }
};
int main()
{
// Create example
Example a;
// Calls the lvalue version of the function since it's called on an lvalue
a.getWhatever();
// Calls rvalue version by making temporary
Example().getWhatever();
return 0;
}
The output of this is:
This is called on lvalue instances of Example
This is called on rvalue instances of Example
Since in the first function call we are calling it on an lvalue instance of Example, but in the second call we are making it a temporary on which we call the function, invoking the rvalue qualified function.
Related
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).
This question already has answers here:
What is "rvalue reference for *this"?
(3 answers)
Closed 4 years ago.
By mistake, I had a & at the end of a prototype (see example below). Neither gcc nor Clang complains about it. I noticed that the symbol generated is not exactly the same.
Example:
class A
{
public:
void fn() &
{
return;
}
};
int main()
{
A a;
a.fn();
return 1;
}
Name of the symbol without &: _ZN1A2fnEv and with &: _ZNR1A2fnEv.
What does it mean ? Do I miss something ?
Thanks,
The & at the end of the member function declaration is a ref qualifier. It applies to the object value on which the member function is called, and constrains that value's value category:
Functions without ref qualifiers can be called on any value.
Functions with & qualifier can only be called on lvalues.
Functions with && qualifier can only be called on rvalues.
The ref qualifier affects overload resolution, e.g. an overload on a mismatched instance value is not viable.
The standard library doesn't use ref qualifiers much (I can only think of std::optional), so let's make up our own example:
struct X {
explicit X(int n) : s_(n, 'a') {}
std::string s_;
const char* f1() const { return s_.c_str(); }
const char* f2() const & { return s_.c_str(); }
const char* f3() const && { return s_.c_str(); }
};
Now consider the following calls:
int main() {
X x(10);
x.f1(); // OK
X(20).f1(); // OK
x.f2(); // OK
X(20).f2(); // ill-formed
x.f3(); // ill-formed
X(20).f3(); // OK
}
The example also demonstrates why this feature may be useful: When a member function returns a reference to some internal part of the object itself, then it is important that that internal reference does not outlast the lifetime of the object. If your member function is unqualified, then you can very easily introduce lifetime bugs. For example:
const char* s = std::string("abcde").c_str(); // dangling pointer!
One way to improve such "internal state access" APIs is to create different return value (categories) for different ref-qualified overloads. To get back to std::optional, the engaged-access essentially boils down to this set of overloads:
struct MyOptional {
T value_; // assume engaged!
T& get() & { return value_; }
T&& get() && { return std::move(value_); }
};
That means that MyOptional::get returns an lvalue when invoked on an lvalue optional, and an rvalue (in fact an xvalue) when invoked on an rvalue. This means that, given MyOptional x;, the binding T& r = x.get(); is allowed, but T& r = MyOptional().get(); is not, and similarly T&& r = x.get(); is disallowed, but T&& r = std::move(x).get() is allowed.
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 found in a C++ book the following:
Although we will not be doing it in this book, you can overload a
function name (or operator) so that it behaves differently when used
as an l-value and when it is used as an r-value. (Recall that an
l-value means it can be used on the left-hand side of an assignment
statement.) For example, if you want a function f to behave
differently depending on whether it is used as an l-value or an
r-value, you can do so as follows:
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
I tried this and it didn't work:
class Foo {
public:
int& id(int& a);
const int& id(int& a) const;
};
int main() {
int a;
Foo f;
f.id(a) = 2;
a = f.id(a);
cout << f.id(a) << endl;
}
int& Foo :: id(int& a) {
cout << "Bar\n";
return a;
}
const int& Foo :: id(int& a) const {
cout << "No bar !\n";
return a;
}
Have I wrongly understood it ?
Either the book's example is flat-out wrong, or you copied the wrong example from the book.
class SomeClass {
public:
int& f(); // will be used in any l-value invocation const
const int& f( ) const; // used in any r-value invocation ...
};
With this code, when you call s.f() where s is an object of type SomeClass, the first version will be called when s is non-const, and the second version will be called when s is const. Value category has nothing to do with it.
Ref-qualification looks like this:
#include <iostream>
class SomeClass {
public:
int f() & { std::cout << "lvalue\n"; }
int f() && { std::cout << "rvalue\n"; }
};
int main() {
SomeClass s; s.f(); // prints "lvalue"
SomeClass{}.f(); // prints "rvalue"
}
Ofcourse the book is correct. Let me explain the workings of an example of what the author meant :
#include <iostream>
using namespace std;
class CO
{
int _m;
public:
CO(int m) : _m(m) {}
int& m() { return _m; } // used as an l-value
int const& m() const { return _m; } // used as an r-value
};
int main()
{
CO a(1);
cout << a.m() << endl;
a.m() = 2; // here used as an l-value / overload resolution selects the correct one
cout << a.m() << endl;
return 0;
}
Output is
1
2
What you misunderstood is the function signature. You see when you have an argument &arg (as in id(&arg)) you pretty much predefine the l-valuness of it, so returning it through a const or non const member function does not change a thing.
The author refers to a common writting style that allows for 'getters' and 'setters' to be declared with a signature different only in const qualifires yet compile and behave correctly.
Edit
To be more pedantic, the following phrase
Recall that an l-value means it can be used on the left-hand side of an assignment statement.
is not valid anymore. lr valuness applies to expressions, and the shortest way to explain it, is that an expression whose adress we can take, is an l-value; if it's not obtainable it's an r-value.
So the syntax to which the author refers to, enforces the member function to be used correctly (correct compilation / overload resolution) at both sides of the assignment operator. This nowdays is no longer relevant to lr valueness.
A const member function can only be called on a const object. It makes no difference what you do with the return value. In your example, f is non-const, so it always calls the non-const version of f(). Note that you can also overload on r-value references (&&) in C++11.
This question already has answers here:
What does the single ampersand after the parameter list of a member function declaration mean?
(3 answers)
Closed 8 years ago.
I have seen in an anwser there:
Is returning by rvalue reference more efficient?
The member function definition:
Beta_ab const& getAB() const& { return ab; }
I am familiar with the cv-qualifier (const) on member functions, but not const&.
What does the last const& mean?
The & is a ref-qualifier. Ref-qualifiers are new in C++11 and not yet supported in all compilers, so you don't see them that often currently. It specifies that this function can only be called on lvalues (and not on rvalues):
#include <iostream>
class kitten
{
private:
int mood = 0;
public:
void pet() &
{
mood += 1;
}
};
int main()
{
kitten cat{};
cat.pet(); // ok
kitten{}.pet(); // not ok: cannot pet a temporary kitten
}
Combined with the cv-qualifier const, it means that you can only call this member function on lvalues, and those may be const.
We know that in this code...
Beta_ab const& getAB() const { return ab; }
^^^^^
The highlighted const means that the member function may be called upon a const object. A member function can always be called upon a non-const object regardless of the function's cv-qualification.
So in this code...
Beta_ab const& getAB() const & { return ab; }
^
We should expect that the highlighted & also says something about what kinds of objects this member function is allowed to be called upon. We would be correct; in C++11, this says that the member function may only be called upon lvalues.
Beta_ab const& getAB() const& { return ab; }
Beta_ab && getAB() && { return ab; }
In the above example, the first overload is invoked on lvalues, and the second overload is invoked on non-const rvalues. Similar to the following more familiar example, with qualifiers applied to ordinary function parameters:
void setAB(AB const& _ab) { ab = _ab; }
void setAB(AB && _ab) { ab = std::move(_ab); }
It works slightly differently for ordinary parameters though, as in this example, the first overload would accept an rvalue if the second overload were removed.