What's the difference in practice between LVALUE and RVALUE in the following code when I pass the text?
I mean, in this specific case of a string (where the string is a string literal), is there any benefit of using RVALUE (&&)?
void write_Lvalue(const std::string &text) {
//...
}
void write_Rvalue(const std::string &&text) {
//...
}
int main() {
write_Lvalue("writing the Lvalue");
write_Rvalue("writing the Rvalue");
}
First, constant rvalue reference are not really useful, since you cannot move them. Moving value need mutable references to work.
Let's take your corrected example:
void write_lvalue(std::string const& text) {
//...
}
void write_rvalue(std::string&& text) {
//...
}
int main() {
write_lvalue("writing the Lvalue");
write_rvalue("writing the Rvalue");
}
In this case, the two are completely equivalent. In these two case, the compiler has to create a string and send it by reference:
int main() {
// equivalent, string created
// and sent by reference (const& bind to temporaries)
write_lvalue(std::string{"writing the Lvalue"});
// equivalent, string created
// and sent by reference (&& bind to temporaries)
write_rvalue(std::string{"writing the Rvalue"});
}
So why have function that takes rvalue references?
It depends on what you do with the string. A mutable reference can be moved from:
std::string global_string;
void write_lvalue(std::string const& text) {
// copy, might cause allocation
global_string = text;
}
void write_rvalue(std::string&& text) {
// move, no allocation, yay!
global_string = std::move(text);
}
So why using rvalue reference at all? Why not using mutable lvalue reference?
That is because mutable lvalue references cannot be bound to temporaries:
void write_lvalue_mut(std::string& text) {
// move, no allocation... yay?
global_string = std::move(text);
}
int main() {
std::string s = /* ... */;
write_lvalue_mut(std::move(s)); // fails
write_lvalue_mut("some text"); // also fails
}
But mutable rvalue reference can be bound to rvalue, as shown above.
There's no benefit in this case. write_Rvalue will only accept an rvalue. and write_Lvalue will only accept an lvalue.
When you pass a string literal a temporary std::string will be constructed from the string literal. The rvalue variant can already bind to this because you're already passing a temporary and the lvalue variant can bind to the temporary because it's const.
This for example, will not compile:
void write_Lvalue(const std::string &text) {
//...
}
void write_Rvalue(const std::string &&text) {
//...
}
int main() {
std::string a = "hello";
write_Rvalue(a);
}
because we're trying to pass an lvalue a to a function only accepting an rvalue.
The benefit that can be gained with rvalue types is that they can be moved from. There's a great post on why moving can be faster here.
Making your rvalue const defeats the purpose of it though as said in the comments, because it can't be moved from anymore.
Related
I have a function f that takes a string as input. I usually want to provide a string literal, e.g., f("hello"). However, I want to implement another function g that builds upon f:
std::string f(const std::string&& x) {
return x + " world";
}
std::string g(const std::string&& x) {
std::string res = f(x); // problem: rvalue reference to std::string cannot bind to lvalue of type std::string
res += "!";
return res;
}
int main() {
std::string res_a = f("hello");
std::string res_b = g("world");
return 0;
}
How can I achieve this in C++11/14 in a way that I can use f with string literals as well as variables?
A generic way of solving the problem of a function taking both l-value and r-value references is to use templated functions like so-
template <typename T>
T f(T&& val) {
}
template <typename T>
T g(T&& val) {
T some_val = f(std::forward<T>(val));
}
std::foward<T>(val) forwards an l-value as an l-value and an r-value as an r-value, just as its name implies.
By templating the function, you ensure that this logic works for any type and not just strings.
The traditional way to take a read-only parameter is by const lvalue reference.
std::string f(const std::string& x)
This rule of thumb applies to many types, not just std::string. The primary exceptions are types that are not bigger than a pointer (e.g. a char).
It's rather unusual for a function to have a const rvalue reference. As you discovered, that adds difficulty when trying to pass a variable as the argument. A non-const rvalue reference has value, but a const rvalue reference is inferior to a const lvaue reference in most cases. See also Do rvalue references to const have any use?
I just found an interesting case: I passed an object by const reference and still I was able to modify its member (which happens to be an lvalue reference). Following is an example:
#include <iostream>
#include <string>
struct PersonRef
{
PersonRef(std::string& name_) : _name(name_) {}
std::string& _name; // <==== IMPORTANT: it is a reference
};
void testRef(const PersonRef& pr) {
std::string& name = pr._name; // binding to lvalue reference? How does it even compile?
name = "changed!";
}
int main() {
std::string name = "trivial_name";
PersonRef pr{name};
std::cout << pr._name << "\n"; // prints: trivial_name
testRef(pr);
std::cout << pr._name << "\n"; // prints: changed!
}
I used to think that if the parameter is passed by const ref, then object is immutable but this doesn't appear to be the case here. Could someone please explain this? Thanks!
Note that in const member function, the data member _name itself will be considered as const. This doesn't make any difference on _name because it's a reference, which can't be const-qualified. (In a sence the reference is always const, you can't modify the reference itself, the reference can't be rebound to other object after initialization.)
On the other hand, the referenced object won't be become const, so it's still possible to be modified, _name won't become const std::string& (reference to const).
Similar thing happens on pointer members; in const member function they become const pointers, but not pointers to const; you still could modify the pointed objects if they're non-consts from the beginning.
Consider a different example.
Let's say you have struct A {int *x;};.
When accessed through const A &ref, x would have type int *const - a const pointer to (non-const) int.
As you can see, const is not added recursively to the pointer. It's only added at the top level.
Back to your code. Strictly speaking, const PersonRef& pr is not a const reference. It's a non-const reference to const PersonRef.
(In theory, a const reference would be written as PersonRef &const pr. But since references are not rebindable to begin with, adding const wouldn't do anything, and thus isn't allowed. So technically references are never const, even though you can't rebind them.)
The compiler can't add const-ness to to std::string& _name, since references can't be const. And it doesn't add const-ness recursively to the referenced type, simply because that's how the language works.
If you don't want this behavior, make std::string& _name private and add a pair of const and non-const accesors:
class PersonRef
{
std::string &_name;
public:
PersonRef(std::string& name_) : _name(name_) {}
std::string &name() {return _name;}
const std::string &name() const {return _name;}
};
Working from the Efficient Modern C++, Item 25. we have an example
Case 1
class Widget {
public:
template<typename T>
void setName(T&& newName)
{ name = std::forward<T>(newName); }
...
};
Case 2
class Widget {
public:
void setName(const std::string& newName)
{ name = newName; }
void setName(std::string&& newName)
{ name = std::move(newName); }
...
};
The call
Widget w;
w.setName("Adela Novak");
Now assuming case 1, the book states that the literal is conveyed to the assignment operator for t std::string inside w's name data member.
Assuming case 2, the book states that -> first a temporary is created from the literal, calling the string constructor, so the setName parameter can bind to it, and than this temporary is moved into w's name data member.
Question
Why does this difference in behavior come about and how am I to think about it?
Namely, why is there no need for a temporary in case 1? Why is there difference? Is T&& not deduced to be an rvalue reference to a string, thus arriving at the same behavior as case 2 (obviously not, as per the book, but why)?
In case 1, T is deduced to be const char (&)[12], not std::string. There is no reason for the compiler to promote the string literal to std::string yet. In case 2, every overload requires takes a reference to an std::string, which forces the creation of a temporary std::string to which a reference can be bound using the implicit const char* constructor.
Note that while an rvalue reference such as std::string && may only bind to an rvalue, the templated equivalent T && may bind to both rvalues and lvalues.
The code below results in Undefined Behaviour. Be sure to read ALL the answers for completeness.
When chaining an object via the operator<< I want to preserve the lvalue-ness / rvalue-ness of the object:
class Avenger {
public:
Avenger& operator<<(int) & {
return *this;
}
Avenger&& operator<<(int) && {
return *this; // compiler error cannot bind lvalue to rvalue
return std::move(*this);
}
};
void doJustice(const Avenger &) {};
void doJustice(Avenger &&) {};
int main() {
Avenger a;
doJustice(a << 24); // parameter should be Avenger&
doJustice(Avenger{} << 24); // parameter should be Avenger&&
return 0;
}
I cannot simply return *this which implies that the type of *this of an rvalue object is still an lvalue reference. I would have expected to be an rvalue reference.
Is it correct / recommended to return std::move(*this) on an member overloaded for the && qualifier, or should other method be used? I know that std::move is just a cast, so I think it’s ok, I just want to double check.
What is the reason/explanation that *this of an rvalue is an lvalue reference and not an rvalue reference?
I remember seeing in C++14 something about move semantics of *this. Is that related to this? Will any of the above change in C++14?
The type of this depends on the cv-qualifier of the member function: Avenger* or const Avenger*
But not on its ref-qualifier. The ref-qualifier is used only to determine the function to be called.
Thus, the type of *this is Avenger& or const Avenger&, no matter if you use the && or not. The difference is that the overload with && will be used then the called object is a r-value, while the & will not.
Note that rvalue-ness is a property of the expression, not the object. For example:
void foo(Avenger &x)
{
foo(x); //recursive call
}
void foo(Avenger &&x)
{
foo(x); //calls foo(Avenger &)!
}
That is, although in the second foo(), x is defined as an r-value reference, any use of the expression x is still an l-value. The same is true for *this.
So, if you want to move out the object, return std::move(*this) is The Right Way.
Could things have been different had this been defined as a reference value instead of as a pointer? I'm not sure, but I think that considering *this as an r-value could lead to some insane situations...
I didn't hear of anything changing about this in C++14, but I may be mistaken...
std::move is perhaps better called rvalue_cast.
But it is not called that. Despite its name, it is nothing but an rvalue cast: std::move does not move.
All named values are lvalues, as are all pointer dereferences, so using std::move or std::forward (aka conditional rvalue cast) to turn a named value that is an rvalue reference at point of declaration (or other reasons) into an rvalue at a particular point is kosher.
Note, however, that you rarely want to return an rvalue reference. If your type is cheap to move, you usually want to return a literal. Doing so uses the same std::move in the method body, but now it actually triggers moving into the return value. And now if you capture the return value in a reference (say auto&& foo = expression;), reference lifetime extension works properly. About the only good time to return an rvalue reference is in an rvalue cast: which sort of makes the fact that move is an rvalue cast somewhat academic.
This answer is in response to bolov's comment to me under his answer to his question.
#include <iostream>
class Avenger
{
bool constructed_ = true;
public:
Avenger() = default;
~Avenger()
{
constructed_ = false;
}
Avenger(Avenger const&) = default;
Avenger& operator=(Avenger const&) = default;
Avenger(Avenger&&) = default;
Avenger& operator=(Avenger&&) = default;
Avenger& operator<<(int) &
{
return *this;
}
Avenger&& operator<<(int) &&
{
return std::move(*this);
}
bool alive() const {return constructed_;}
};
void
doJustice(const Avenger& a)
{
std::cout << "doJustice(const Avenger& a): " << a.alive() << '\n';
};
void
doJustice(Avenger&& a)
{
std::cout << "doJustice(Avenger&& a): " << a.alive() << '\n';
};
int main()
{
Avenger a;
doJustice(a << 24); // parameter should be Avenger&
doJustice(Avenger{} << 24); // <--- this one
// Avenger&& dangling = Avenger{} << 24;
// doJustice(std::move(dangling));
}
This will portably output:
doJustice(const Avenger& a): 1
doJustice(Avenger&& a): 1
What the above output demonstrates is that a temporary Avenger object will not be destructed until the sequence point demarcated by the ';' just before the comment "// <--- this one" above.
I've removed all undefined behavior from this program. This is a fully conforming and portable program.
It is NOT ok to return std::move(*this) on a member overloaded for the && qualifier. The problem here is not with the std::move(*this) (which other answers correctly show that it is ok) but with the return type. The problem is very subtle and it was almost done by the c++11 standardization committee. It is explained by Stephan T. Lavavej in his presentation Don’t Help the Compiler during Going Native 2013. His example can be found at around minute 42 in the linked video. His example is slightly different and doesn’t involve *this and uses overload by parameter reference type rather than by method ref qualifiers but the principle is still the same.
So what is wrong with the code?
Short introduction: a reference bound to a temporary object prolongs the lifetime of the temporary object for the lifetime of the reference. That is what makes code like this be ok:
void foo(std::string const & s) {
//
}
foo("Temporary std::string object constructed from this char * C-string");
The important part here is that this property is not transitive, meaning that for the reference to prolong the lifetime of the temporary object, it must bind directly to the temporary object, and not to a reference to it.
Returning to my example:
For completness let’s add a function that takes only a const lvalue reference to Avenger (no rvalue reference overload):
void doInjustice(Avenger const &) {};
the next two calls result in UB if referencing the parameter inside the functions:
doInjustice(Avenger{} << 24); // calls `void doInustice(Avenger const &) {};`
doJustice(Avenger{} << 24); // calls `void doJustice(Avenger &&) {};`
The temporary objects constructed at parameter evaluation are destroyed as soon as the function are called for the reasons exposed above and the parameters are dangling references. Referencing them inside the functions will result in UB.
The correct way is to return by value:
class Avenger {
public:
Avenger& operator<<(int) & {
return *this;
}
Avenger operator<<(int) && {
return std::move(*this);
}
};
A copy is still eluded with the move semantics, and the return is a temporary, meaning that it will call the correct overload, but we avoid this subtle but nasty silent bug.
The example of Stephan T. Lavavej: Don’t Help the Compiler (42m–45m)
string&& join(string&& rv, const char * ptr) {
return move(rv.append(", ").append(ptr));
}
string meow() { return "meow"; }
const string& r = join(meow(), "purr");
// r refers to a destroyed temporary!
//Fix:
string join(string&& rv, const char * ptr) {
return move(rv.append(", ").append(ptr));
}
Posts on SO explaining the prolonging of life of temporary objects through references:
Does a const reference prolong the life of a temporary?
Initializing a reference
My git copy of the C++ Standard (page 290, section15.2, point 6) says:
"The exceptions to this lifetime rule [of a reference binding to a
temporary extending the lifetime of that temporary] are:
sub 9 - A temporary object bound to a reference parameter in a
function call persists until the completion of the
full-expression containing the call.
sub 10 - The lifetime of a temporary bound to the returned value
in a function return statement (9.6.3) is not extended;
the temporary is destroyed at the end of the
full-expression in the return statement. [...]"
Therefore, the implicit this `parameter' passed to
Avenger&& operator<<(int) &&
{
return std::move(*this);
}
ends its life at the ; of the statement that calls operator<< on a temporary object, even if a reference is bound to it. So this fails:
Avenger &&dangler = Avenger{} << 24; // destructor already called
dangler << 1; // Operator called on dangling reference
If OTOH the return is by value:
Avenger operator<<(int) &&
{
return std::move(*this);
}
then none of that misery happens, and there is often no extra cost in terms of copying. And indeed, if you don't offer the compiler to create that return value by move-constructing from *this, then it 'll make a copy.
So snatch *this from the brink of oblivion, and have the best of both worlds.
I was reading C++ Faq Second Edition , faq number 32.08 .
FAQ says that parameter passed by const reference and returned by const reference can cause dangling reference.
But it is ok if parameter is passed by reference and returned by reference.
I got it that it is unsafe in case of const reference but how is it safe in case when parameter is non const reference.
Last line of FAQ says
"Note that if a function accepts a parameter by non-const reference (for example, f(string& s)), returning a copy of this reference parameter is safe because a temporary cannot be passed by non-const reference."
Need some insight on this!!
if you have like
const Foo & bar(const Foo &f) { return f; }
and call it like
const Foo &ret = bar(Foo());
This compiles, but the problem is that now 'ret' is a dangling reference, because the temporary object created by the call to Foo() gets freed after bar returns. The detailed execution sequence here is:
temporary Foo is allocated
bar is called with a reference to the temporary object
bar returns the reference
now that bar has returned the temporary Foo is released
the reference is now dangling as the object was destroyed
However, if you had Foo declared as
Foo & bar(Foo &f) { return f; }
then your call bar(Foo()) would not be accepted by compiler. When you pass a temporary object to a function, you can only take it by const reference or as a copy; this is part of the language definition.
Temporaries can be passed by const reference - when the function returns the temporaries are destoyed, so the caller is left with a dangling ref.
For example:
#include <iostream>
using namespace std;
int const& output( int const& x)
{
cout << x << endl;
return x;
}
int main ()
{
int a = 1;
int const& ref1 = output( a); // OK
int const& ref2 = output(a+1); // bad
return 0;
}
I think this example will be helpful:
const int& f(const int& n)
{
return n;
}
int f1(int& n)
{
return n;
}
int main(int argc, char **argv)
{
//Passing a reference to an anonymous object created by
//the compiler to function f()
const int& n = f(10);
//Undefined behavior here as the scope of the anonymous object
//was only till previous statement
int k = n + 10;
//Compiler error - Can not pass non-const reference to a anonymous object
int n = f1(10);
}
Here is a page about C++0x rvalue references that starts with a pretty decent summary of how lvalues and rvalues work in C++, along with how they are allowed to bind with references.
Most articles you will find about rvalue references in C++0x will give you some insight into this.