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

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.

Related

Is repeatedly calling move with rvalue references necessary?

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.

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.

Is it good practice to bind shared pointers returned by functions to lvalue references to const?

Although it took me a while to get used to it, I now grew the habit of letting my functions take shared pointer parameters by lvalue-reference to const rather than by value (unless I need to modify the original arguments, of course, in which case I take them by lvalue-reference to non-const):
void foo(std::shared_ptr<widget> const& pWidget)
// ^^^^^^
{
// work with pWidget...
}
This has the advantage of avoiding an unnecessary copy of a shared pointer, which would mean thread-safely increasing the reference counting and potentially incurring in undesired overhead.
Now I've been wondering whether it is sane to adopt a somewhat symmetrical habit for retrieving shared pointers that are returned by value from functions, like at the end of the following code snippet:
struct X
{
// ...
std::shared_ptr<Widget> bar() const
{
// ...
return pWidget;
}
// ...
std::shared_ptr<Widget> pWidget;
};
// ...
// X x;
std::share_ptr<Widget> const& pWidget = x.bar();
// ^^^^^^
Are there any pitfalls with adopting such a coding habit? Is there any reason why I should prefer, in general, assigning a returned shared pointer to another shared pointer object rather than binding it to a reference?
This is just a remake of the old question of whether capturing a const reference to a temporary is more efficient than creating a copy. The simple answer is that it isn't. In the line:
// std::shared_ptr<Widget> bar();
std::shared_ptr<Widget> const & pWidget = bar();
The compiler needs to create a local unnamed variable (not temporary), initailize that with the call to bar() and then bind the reference to it:
std::shared_ptr<Widget> __tmp = bar();
std::shared_ptr<Widget> const & pWidget = __tmp;
In most cases it will avoid the creation of the reference and just alias the original object in the rest of the function, but at the end of the day whether the variable is called pWidget or __tmp and aliased won't give any advantage.
On the contrary, for the casual reader, it might look like bar() does not create an object but yield a reference to an already existing std::shared_ptr<Widget>, so the maintainer will have to seek out where bar() is defined to understand whether pWidget can be changed outside of the scope of this function.
Lifetime extension through binding to a const reference is a weird feature in the language that has very little practical use (namely when the reference is of a base and you don't quite care what the exact derived type returned by value is, i.e. ScopedGuard).
You may have the optimization backwards:
struct X
{
// ...
std::shared_ptr<Widget> const& bar() const
{
// ...
return pWidget;
}
// ...
std::shared_ptr<Widget> pWidget;
};
// ...
// X x;
std::share_ptr<Widget> pWidget = x.bar();
As bar is returning a member variable, it must take a copy of the shared_ptr in your version. If you return the member variable by reference the copy can be avoided.
This doesn't matter in both your original version and the version shown above, but would come up if you called:
x.bar()->baz()
In your version a new shared_ptr would be created, and then baz would be called.
In my version baz is called directly on the member copy of the shared_ptr, and the atomic reference increment/decrement is avoided.
Of course the cost of the shared_ptr copy constructor (atomic increment) is very small, and not even noticable in all but the most performance-sensetive applications. If you are writing a performance sensetive application than the better option would be to manage memory manually with a memory pool architecture and then to (carefully) use raw pointers instead.
Adding on top of what David Rodríguez - dribeas said namely, binding to a const reference doesn't save you from making the copy and the counter is incremented anyway, the following code illustrates this point:
#include <memory>
#include <cassert>
struct X {
std::shared_ptr<int> p;
X() : p{new int} {}
std::shared_ptr<int> bar() { return p; }
};
int main() {
X x;
assert(x.p.use_count() == 1);
std::shared_ptr<int> const & p = x.bar();
assert(x.p.use_count() == 2);
return 0;
}

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() );
}

How to return member that can be changed?

Multi thread enviroment . The content of Foo can be multi thread.
class Foo
{
public:
const A & getA() {return a_;} //has guard
void setA(A newA){a_ = newA;} //has guard
private:
A a_;
};
caller:
A a = foo.getA();
in another question that i asked someone told me that
If you return const& it's guaranteed that life time of variable will be prolonged to lifetime of the reference
, so according to this i dont need to copy the value and i safe even if call to setA on foo done right after i call to getA, but a lot of argument against it was araised , so i feel that this is not correct.
I want to be on the safe side so i change the signature to be :
A & getA() {return a_;}
but the compiler still give me warning that i have reference to local variable. i dont understand why, because as far as i understand (new to cpp) the return value is a copy of foo.a, so what the problem with this?
i am not worried about change of a_ content.(_a.age =4) . i worry about call to set and that my 'a' in the caller will be illegal
You need to be more careful who you listen to. The only time the lifetime of something gets extended if a temporary object is bound immediately to a const-reference. For example, like so:
Foo bar() { return Foo(); }
int main()
{
Foo const & f = bar();
/* stuff */
} // the object referred to by f is extended till here
Your situation is nothing like that. In particular, returning a const-reference does not create a temporary object, so there's nothing here who's life gets prolonged. In particular, the following is definitely an error:
A const & bar() { Foo x; return x.getA(); }
int main()
{
A const & a = bar(); // dangling reference; object dies upon return from Foo::getA()
}