Is repeatedly calling move with rvalue references necessary? - c++

struct Bar
{
Bar(std::string&& val)
: m_Val(std::move(val)) {} // A
Bar& operator=(Bar&& _other) { m_Val = std::move(_other.m_Val); }
std::string m_Val;
}
struct Foo
{
void Func1(Bar&& param)
{
Func2(std::move(param)) // B
}
void Func2(Bar&& param)
{
m_Bar = std::move(param); // C
}
Bar m_Bar;
};
void main()
{
Foo f;
std::string s = "some str";
Bar b(std::move(s));
f.Func1(std::move(b));
}
Give that you're calling move in main() to invoke the rvalue reference methods, is it necessary in lines A & B & C to repeat an additional call to move()? You already have the rvalue reference, so is it doing anything different in those lines with vs without?
I understand in Bar's operator= it's necessary because you're technically moving the m_Val rather than _other itself correct?
Note: Originally, I was incorrectly calling rvalue references as rvalue parameters. My apologies. I've corrected that to make the question easier to find and make clearer.

Give that you're calling move in main() to invoke the rvalue parameter methods, is it necessary in lines A & B & C to repeat an additional call to move()?
Yes. What you call an rvalue parameter is actually an rvalue reference. Just like a lvalue reference, it is an lvalue in the scope that it is being used. That means you need to use move to cast it back into an rvalue so that it gets moved, instead of copied. Remember, if the object has a name, it is an lvalue.

Related

Why can an rvalue not bind to a non-const lvalue reference, other than the fact that writing to a temporary has no effect?

I have read the SO question here and understood this part of the answer: "But if you bind a temporary to a non-const reference, you can keep passing it around "forever" just to have your manipulation of the object disappear, because somewhere along the way you completely forgot this was a temporary."
That is, in the following:
#include <iostream>
void modifyValue(int& rValue) {
rValue++;
}
int main() {
modifyValue(9899);
return 0;
}
If an rvalue could bind to a non-const lvalue reference, then potentially many modifications could be done that would eventually be discarded (since an rvalue is temporary), this being useless.
This seems to be well defined however (writing to a temporary value is just like writing to any value, the lifetime has no relevancy to the validity of the writing).
That is a perfectly alright reason to prohibit the specified binding (even though the binding would be well defined), however once I considered that such a binding being prohibited forces the need for forwarding references, my question started forming.
Are there any other reasons (that is, apart from writing to a temporary value) as to why an rvalue cannot bind to a non-const lvalue reference?
The simple answer is that in most cases, passing a temporary to a function that expects a mutable lvalue reference indicates a logic error, and the c++ language is doing its best to help you avoid making the error.
The function declaration: void foo(Bar& b) suggests the following narrative:
foo takes a reference to a Bar, b, which it will modify. b is therefore both an input and an output
Passing a temporary as the output placeholder is normally a much worse logic error than calling a function which returns an object, only to discard the object unexamined.
For example:
Bar foo();
void test()
{
/*auto x =*/ foo(); // probable logic error - discarding return value unexamined
}
However, in these two versions, there is no problem:
void foo(Bar&& b)
foo takes ownership of the object referenced by Bar
void foo(Bar b)
foo conceptually takes a copy of a Bar, although in many cases the compiler will decide that creating and copying a Bar is un-necessary.
So the question is, what are we trying to achieve? If we just need a Bar on which to work we can use the Bar&& b or Bar b versions.
If we want to maybe use a temporary and maybe use an existing Bar, then it is likely that we would need two overloads of foo, because they would be semantically subtly different:
void foo(Bar& b); // I will modify the object referenced by b
void foo(Bar&& b); // I will *steal* the object referenced by b
void foo(Bar b); // I will copy your Bar and use mine, thanks
If we need this optionality, we can create it by wrapping one in the other:
void foo(Bar& b)
{
auto x = consult_some_value_in(b);
auto y = from_some_other_source();
modify_in_some_way(b, x * y);
}
void foo(Bar&& b)
{
// at this point, the caller has lost interest in b, because he passed
// an rvalue-reference. And you can't do that by accident.
// rvalues always decay into lvalues when named
// so here we're calling foo(Bar&)
foo(b);
// b is about to be 'discarded' or destroyed, depending on what happened at the call site
// so we should at least use it first
std::cout << "the result is: " << b.to_string() << std::endl;
}
With these definitions, these are now all legal:
void test()
{
Bar b;
foo(b); // call foo(Bar&)
foo(Bar()); // call foo(Bar&&)
foo(std::move(b)); // call foo(Bar&&)
// at which point we know that since we moved b, we should only assign to it
// or leave it alone.
}
OK, by why all this care? Why would it be a logic error to modify a temporary without meaning to?
Well, imagine this:
Bar& foo(Bar& b)
{
modify(b);
return b;
}
And we're expecting to do things like this:
extern void baz(Bar& b);
Bar b;
baz(foo(b));
Now imagine this could compile:
auto& br = foo(Bar());
baz(br); // BOOM! br is now a dangling reference. The Bar no longer exists
Because we are forced to handle the temporary properly in a special overload of foo, the author of foo can be confident that this mistake will never happen in your code.

Understanding the warning: binding r-value to l-value reference

I want to pass a struct by reference so it won't be copied, but Resharper is giving the warning below:
struct sometype {
};
sometype foo() {
sometype x;
return x;
}
void bar() {
sometype & a = foo();//Binding r-value to l-value reference is non-standard Microsoft C++ extension
sometype && b = foo(); //ok
}
Questions:
What's wrong with sometype & a = foo(); ? isn't the return value from foo() an lvalue and a is also an lvalue?
Is sometype && b = foo(); actually rvalue reference? Does it "steal" the return value from foo() and send what was in b to the destructor?
Is there another way to not have this warning?
You are taking a reference to a temporary object. The only legal way to do this is either :
const object& (const l-value reference), or
object&& (mutable r-value reference)
This is a (deliberate) language limitation.
further discussion:
Assigning a temporary to a reference extends the lifetime of the temporary so that it matches the lifetime of the reference. Therefore, surprisingly to many beginners, this is legal:
{
const string& s = foo();
cout << s << endl; // the temporary to which s refers is still alive
}
// but now it's destroyed
However, it would normally be a logic error to take a mutable reference to a temporary so this is disallowed in the language:
{
string& s = foo(); // this is not possible
s += "bar"; // therefore neither is this
// the implication is that since you modified s, you probably want to
// preserve it
}
// ... but now it's destroyed and you did nothing with it.
here's a more realistic reason why it's probably a logic error, given:
string foo(); // function returning a string
void bar(string& s); // this function is asserting that it intends to *modify*
// the string you sent it
// therefore:
bar(foo()); // makes no sense. bar is modifying a string that will be discarded.
// therefore assumed to be a logic error
you would have to replace the above with:
string s = foo();
s += "bar";
// do something here with s
Note that there is no overhead whatsoever for capturing the temporary in a named variable (l-value).
r-value references are designed to be the subject of a move-constructor or move-assignment. Therefore it makes sense that they are mutable. Their very nature implies that the object is transient.
thus, this is legal:
string&& s = foo(); // extends lifetime as before
s += "bar";
baz(std::move(s)); // move the temporary into the baz function.
It might help you to remember that specifying && is you asserting that you know that the variable is a mutable temporary.
But the real reason it's allowed is so that this will work:
string foo(); // function that returns a string
void bar(string&& s); // function that takes ownership of s
bar(foo()); // get a string from foo and move it into bar
// or more verbosely:
string s = foo();
bar(move(s));
prior to c++11, bar would have to have been written one of these ways:
void bar(string s); // copy a string
// resulting in:
const string& s = foo();
bar(s); // extra redundant copy made here
void bar(const string& s); // const l-value reference - we *may* copy it
// resulting in:
const string& s = foo();
bar(s); // maybe an extra redundant copy made here, it's up to bar().
What's wrong with sometype & a = foo(); ?
foo() returns temporary so you cannot bind it to reference because it will no longer exists after the end of full expression (assignment line). The only way to extend its life time is to change it to const sometype & a = foo(); Or assign it to rvalue reference.
Is sometype && b = foo(); actually rvalue reference?
yes (read here for more: Do rvalue references allow dangling references?)
Does it "steal" the return value from foo() and send what was in b to the destructor?
no, it extends its lifetime
Is there another way to not have this warning?
You have three choices: (1) assign to rvalue reference, (2) assign to const lvalue reference, (3) return by value but implement move semantics in your class.
You can also count on that compiler will perform RVO on returned value.

C++: use returned object from function

To use returned object from functions, could anyone tell me why case 1, 3, 4 are OK but not 2 in this code?
#include <iostream>
using namespace std;
class X {
int i;
public:
X(int ii = 0) : i(ii) {};
void modify() { i++; };
};
X f1() { return X(1); }
// Pass by non-const reference
void f20(X& x) { x.modify(); }
// Pass by const reference
void f21(const X& x) { }
// Pass by value
void f22(X x) { x.modify(); }
int main() {
f1() = X(2); // 1. OK
//! f20(f1()); // 2. Bad
f21(f1()); // 3. OK
f22(f1()); // 4. OK
}
Thank you!
Because temporaries can't be bound to lvalue reference to non-const.
The main rationale I've seen bandied about is that otherwise (as with the Visual C++ language extension) a function like
void increment( int& value ) { ++value; }
might be called like
auto main() -> int
{
increment( 2+2 );
}
but I'm not sure that's entirely convincing.
The rules do help with much coding, but as the Visual C++ language extension (allowing the binding for class types) exemplify, it's not crucial and not much an issue in ordinary code.
In passing, note that a temporary object is not const, unless it has been declared as such. In particular you can call non-const member functions on it, like in your case f1().modify(). You just can’t bind it to an lvalue reference to non-const.
The f1() method returns the temporary object of type X.
The = operator can be applied to the object of type X, even though the object itself is not going to be available afterwards.
f21() expects const reference (i.e. something that isn't supposed to change within that function) so there is no check whether the reference is valid.
f22() expects the value itself, which is modified inside the function and then the modifications are lost.
f20() expects a reference, i.e. the changes which are made inside this function are supposed to apply to the object that exists outside of that function. But it gets a reference to a temporary object, therefore the problem.
If you want to return the reference to the non-temporary object, your function would have to look like:
X& f1() { return *(new X(1)); }
and you'll have to take care of deleting this object afterwards.

Why does std::reference_wrapper<const T> not accept a temporary?

Normally, rvalues can bind to const references (const SomeType&). It's built into the language. However, std::reference_wrapper<const T> does not accept an rvalue as its constructor argument since the corresponding overload is deliberately deleted. What is the reason for this inconsistency? std::reference_wrapper is "advertised" as the alternative to a reference variable for cases when we must pass by value but would like to preserve reference semantics.
In other words, if the rvalue to const & binding is considered safe, since it's built into the language, why did the designers of C++11 not allow rvalues to be wrapped in std::reference_wrapper<const T>?
When would this come handy, you may ask. For example:
class MyType{};
class Foo {
public:
Foo(const MyType& param){}
};
class MultiFoo {
public:
MultiFoo(std::initializer_list<std::reference_wrapper<const MyType>> params){}
};
int main()
{
Foo foo{MyType{}}; //ok
MultiFoo multiFoo{MyType{}, MyType{}}; //error
}
INTRODUCTION
Normally T const& and T&& can extend the lifetime of a temporary directly bound to it, but this is not applicable if the reference is "hiding" behind a constructor.
Since std::reference_wrapper is copyable (by intention), the handle to the referenced object can outlive the temporary if the std::reference_wrapper is used in such a way that the handle escapes the scope where the temporary is created.
This will lead to a lifetime mismatch between the handle and the referred to object (ie. the temporary).
LET'S PLAY "MAKE BELIEVE"
Imagine having the below, illegal, snippet; where we pretend that std::reference_wrapper has a constructor that would accept a temporary.
Let's also pretend that the temporary passed to the constructor will have its lifetime extended (even though this isn't the case, in real life it will be "dead" right after (1)).
typedef std::reference_wrapper<std::string const> string_ref;
string_ref get_ref () {
string_ref temp_ref { std::string { "temporary" } }; // (1)
return temp_ref;
}
int main () {
string_ref val = get_ref ();
val.get (); // the temporary has been deconstructed, this is dangling reference!
}
Since a temporary is created with automatic storage duration, it will be allocated on the storage bound to the scope inside get_ref.
When get_ref later returns, our temporary will be destroyed. This means that our val in main would refer to an invalid object, since the original object is no longer in existance.
The above is the reason why std::reference_wrapper's constructor doesn't have an overload that accepts temporaries.
ANOTHER EXAMPLE
struct A {
A (std::string const& r)
: ref (r)
{ }
std::string const& ref;
};
A foo { std::string { "temporary " } };
foo.ref = ...; // DANGLING REFERENCE!
The lifetime of std::string { "temporary" } will not be extended, as can be read in the standard.
12.2p5 Temporary objects [class.temporary]
The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
A temporary bound to a reference member in a constructor's ctor-initializer (12.6.2) persists until the constructor exits.
A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the full-expression containing the new-initializer.
Note: it's important to note that a constructor is nothing more than a "fancy" function, called upon object construction.
Binding a temporary directly to a reference prolongs its lifetime.
struct Foo {};
Foo f() { return {}; }
void g() {
f(); // temporary destroyed at end of full-expression
const Foo& r = f(); // temporary destroyed at end of scope
// r can still be used here ...
// ...
// all the way down to here
}
However, it must bind directly to the temporary. Consider the following example:
struct Bar {
Bar(const Foo& f): r(f) {}
const Foo& r;
};
void h() {
Bar b(f());
// binding occurs through parameter "f" rather than directly to temporary "f()"
// b.r is now a dangling reference! lifetime not extended
}
Which would make it quite useless to wrap a temporary, even if you could.
Note: an actual reference wrapper object uses a pointer, so that it can be reassigned:
struct Baz {
Baz(const Foo& f): p(std::addressof(f)) {}
Baz& operator=(const Foo& f) { p = std::addressof(f); return *this; }
const Foo* p;
};
The same still applies: the temporary passed into the constructor or assignment operator will be destroyed at the end of the full-expression containing the call.
You should not copy a const reference as it does not necessarily keep the referenced object alive. The following code shows the problem:
#include <iostream>
struct X { int x; };
struct Foo {
const X& a;
Foo(const X& na) : a(na) {} // here na is still OK
};
int main()
{
Foo foo{X{3}}; // the temporary exists only for this expression
// now the temporary is no longer alive and foo.a is a dangling reference
std::cout << foo.a.x << std::endl; // undefined behavior
}
The const reference keeps the temporary X{3} alive for the constructor of Foo, but not for the object itself. You get a dangling reference.
To protected you from this problem the usage of temporary objects with std::reference_wrapper and std::ref has been disabled.
Since a const T&& variable can nor be moved, neither modified, there's no reason for using it (there's no adventages or differences over a const T&). Even more, a posterior use of that reference can be dangerous if the corresponding temporary is no longer alive (actually, it's dangerous, because it invokes undefined behaviour in such a case).
The deletion of its rvalue-reference constructor is not a bad idea after all.

Is return by value always const?

This code does not compile:
class C {};
void foo (C& c) {}
C bar() { return C(); }
int main()
{
foo(bar());
}
Compilation error (GCC 4.1.2) in line foo(bar()):
invalid initialization of non-const reference of type 'C&'
from a temporary of type 'C'
As bar() returns a mutable object, it should compile...
Why C++ does not allow this above code?
EDIT: I have summarize in an answer below all good ideas from all answers ;-)
The applicable rule here is that you can't create a non-const reference to a temporary object. If foo was declared as foo(const C&) the code would be okay.
The temporary object itself is not const, though; you can call non-const member functions on it, e.g., bar().non_const_member_function().
With C++11, foo can be written to take an rvalue reference; in that case, the call would be okay:
void foo(C&&);
foo(bar()); // okay
It's because the value returned by bar is a temporary value. As it's existence is temporary, you can't use a pointer or reference to that.
However, if you store a copy of that temporary, as in your second change, you no longer pass a reference to a temporary object to foo, but a reference to a real tangible object. And in the first case, when you change to a reference to a constant object, the compiler makes sure the temporary object stays around long enough (as per the C++ specification).
The issue is not with the declaration of bar but with that of foo. foo takes a non-const reference, and temporaries can only bind to const references (which then extends the lifetime of the temporary to match that of the reference it is bound to).
Allowing a non-const reference to bind to a temporary doesn't make much sense. A non-const reference implies that it will modify whatever object is bound to it. Modifying a temporary serves no purpose since its lifetime is limited and the changes will be lost as soon as it goes out of scope.
Modifiable (lvalue-)references do not bind to temporary values. However, const-references do bind to temporary values. It has nothing to do with whether the object returned by value is const or not; it's simply a matter of whether the expression is temporary or not.
For example, the following is valid:
struct C { void i_am_non_const() {} };
int main()
{
bar().i_am_non_const();
}
It is a design choice. There is nothing inherently impossible here. Just a design choice.
In C++11, you have a third alternative which is also superior alternative:
void foo(C && c) {}
That is, use rvalue-references.
It's not const, but it is a temporary rvalue. As such, it can't bind to a non-const lvalue reference.
It can bind to a const or rvalue reference, and you can call member functions (const or not) on it:
class C { void f(); };
void foo_const(C const &);
void foo_rvalue(C &&);
foo_const( bar() ); // OK
foo_rvalue( bar() ); // OK
bar().f(); // OK
The real, hard truth is that it makes no sense to get a reference to a temporary value.
The big point of passing an object by reference is that it allows you to modify its state. However, in the case of a temporary, by its very nature, it would not be particularly helpful to be able to modify it, since you have no way of getting another reference to it later in your code to see the changes.
However, this is somewhat different in the case you have a const reference. Since you'll only ever read from a const reference, it makes total sense to be able to use temporaries there. This is why the compiler will "hack" around it for you, and give a more permanent address to temporaries that you want to "turn" into const references.
So, the rule is that you cannot get a non-const reference to a temporary value. (This slightly changed with C++11, where we have a new type of references that serve this exact purpose, but methods are expected to deal with those in a special way.)
Thank you all for your answers :-)
Here I gather your good ideas ;-)
Answer
Return by value is not const. For example, we can call non-const member functions of return by value:
class C {
public:
int x;
void set (int n) { x = n; } // non-const function
};
C bar() { return C(); }
int main ()
{
bar.set(5); // OK
}
But C++ does not allow non-const references to temporary objects.
However C++11 allow non-const rvalue-references to temporary objects. ;-)
Explanation
class C {};
void foo (C& c) {}
C bar() { return C(); }
//bar() returns a temporary object
//temporary objects cannot be non-const referenced
int main()
{
//foo() wants a mutable reference (i.e. non-const)
foo( bar() ); // => compilation error
}
Three fixes
Change foo declaration
void foo (const C& c) {}
Use another object
int main()
{
C c;
foo( c = bar() );
}
Use C++11 rvalue-reference
void foo(C && c) {}
Moreover
To confirm temporary objects are const, this above source code fails for the same reason:
class C {};
void foo(C& c) {}
int main()
{
foo( C() );
}