Intuitive understanding of functions taking references of references [duplicate] - c++

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does T&& mean in C++11?
For some reason, this is eluding my intuition, and I cannot find any explanation on the internet. What does it mean for a C++ function to take a reference of a reference? For example:
void myFunction(int&& val); //what does this mean?!
I understand the idea of passing-by-reference, so
void addTwo(int& a)
{
a += 2;
}
int main()
{
int x = 5;
addTwo(x);
return 0;
}
works and is intuitive to me.

This is not a reference of a reference, but rather a new language feature called an rvalue reference that represents (informally) a reference to an object in memory that isn't referenced elsewhere in the program and can be destructively modified. For example, the return value of a function can be captured by an rvalue reference, as can temporary values introduced into expressions.
Rvalue references can be used for a variety of purposes. From the perspective of most C++ programmers, they can be used to implement move semantics, whereby a new object can be initialized by "moving" the contents of an old object out of the old object and into a new object. You can use this to return huge objects from functions in C++11 without paying a huge cost to copy the object, since the object used to capture the return value can be initialized using the move constructor by just stealing the internals from the temporary object created by the return statement.
Move semantics are orthogonal to copy semantics, so objects can be movable without being copyable. For example, std::ofstreams are not copyable, but they will be movable, so you could return std::ofstreams from functions using the move behavior. This currently cannot be done in C++03. For example, this code is illegal in C++03 but perfectly fine (and encouraged!) in C++11:
std::ifstream GetUserFile() {
while (true) {
std::cout << "Enter filename: ";
std::string filename;
std::getline(std::cin, filename);
ifstream input(filename); // Note: No .c_str() either!
if (input) return input;
std::cout << "Sorry, I couldn't open that file." << std::endl;
}
}
std::ifstream file = GetUserFile(); // Okay, move stream out of the function.
Intuitively, a function that takes an rvalue reference is a function that (probably) is trying to avoid an expensive copy by moving the contents of an old object into a new object. For example, you could define a move constructor for a vector-like object by having that constructor take in an rvalue reference. If we represent the vector as a triple of a pointer to an array, the capacity of the array, and the used space, we might implement its move constructor as follows:
vector::vector(vector&& rhs) {
/* Steal resources from rhs. */
elems = rhs.elems;
size = rhs.size;
capacity = rhs.capacity;
/* Destructively modify rhs to avoid having two objects sharing
* an underlying array.
*/
rhs.elems = nullptr; // Note use of nullptr instead of NULL
rhs.size = 0;
rhs.capacity = 0;
}
It's important to notice that when we clear out rhs at the end of the constructor that we end up putting rhs into such a state that
Will not cause a crash when its destructor invokes (notice that we set its element pointer to nullptr, since freeing nullptr is safe), and
Still lets the object be assigned a new value. This latter point is tricky, but it's important to ensure that you can still give the cleared-out object a new value at some point. This is because it is possible to obtain an rvalue reference to an object that can still be referenced later in the program.
To shed some light on (2), one interesting use case for rvalue references is the ability to explicitly move values around between objects. For example, consider this idiomatic implementation of swap:
template <typename T> void swap(T& lhs, T& rhs) {
T temp = lhs;
lhs = rhs;
rhs = temp;
}
This code is legal, but it's a bit unusual. In particular, it ends up making three copies - first when setting temp equal to a copy of lhs, once setting lhs to be a copy of rhs, and once setting rhs to be a copy of temp. But we don't really want to be making any copies at all here; instead, we just want to shuffle the values around. Consequently, in C++11, you'll be able to explicitly get rvalue references to objects by using the std::move function:
template <typename T> void swap(T& lhs, T& rhs) {
T temp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(temp);
}
Now, no copies are made at all. We move the contents of lhs into temp, then move the contents of rhs into lhs, then moves the contents of temp into rhs. In doing so, we left both lhs and rhs in an "emptied" state temporarily before putting new values into them. It's important that when writing the code to move the contents out of an object that we leave the object in a somewhat well-formed state so that this code works correctly.

It's not a reference to a reference. It's a new syntax introduced in C++0x for so-called Rvalue references.

Related

Pass by value/reference/rvalue with a std::move(str) arg

I have the following code:
//void func(const std::string &&i){
//void func(const std::string &i){
void func(const std::string i){
std::string val{i};
}
int main()
{
std::string d = "asdf";
func(std::move(d));
std::cout << d << std::endl;
}
When i is pass-by-value, d becomes empty, but d retains its form if we're passing by reference or by r-value reference. Could someone explain what is going on?
I understand that std::move doesn't actually move anything, but rather makes the variable it takes in moveable by casting it to an xvalue.
As an aside why does the code in the current state compile if d is cast to an x-value? func is currently set to take in pass by value arguments, and not by rvalue reference.
When i is pass-by-value, d becomes empty,
To be accurate, d will be in some valid state not specified in the C++ standard. Empty is one possibility.
std::move itself never causes the move constructor to be called directly. Neither does binding an rvalue reference to an object cause move constructor to be called directly.
Only initialising an object with a non-const rvalue will cause the argument to be moved from. In the example, std::string i is initialised with a non-const rvalue and the move constructor will be called.
As an aside why does the code in the current state compile if d is cast to an x-value?
Because the type has a (non-deleted) move constructor. Therefore the argument can be initialised from an rvalues.
I had thought if we had std::string i, a copy of the rvalue reference is made.
std::string i is not a reference. It is a variable of type std::string and as such there is an object of type std::string associated with the variable. That object is initialised with the expression that is passed into the function as argument.
Also, if I observe that the output of d is still the same as prior to applying std::move, what does this mean in this case?
If you call the uncommented version of the function with an rvalue, then the argument will be moved from. If the value is same as it was, then it simply means that the value is the same. You cannot assume that the value will be the same nor that it won't be the same.
Does it mean that d is still occupying the space it originally occupied?
Assuming that by "space" you mean the storage where the variable is, then of course it is still occupying the same storage. The address of an object never changes through the lifetime of the object.
void func(const std::string &&i)
This signature will not move anything, because the reference is to a const object. Remove the const, and it'll work. But only if you std::move the parameter i again inside the function. This is because anything that has a name is an lvalue, whether the parameter was declared as & or &&. See this answer.
void func(const std::string &i)
This will copy, as you probably already know. However, it behaves similarly to the ptevious one in that if you drop the const and do std::move( i ) inside the function, it'll actually move. This is because, as you noted, move is a cast and the compiler will listen to you and do exactly what you say when you cast, regardless of what you intended.
void func(const std::string i)
This moves in your example because here, i is an entirely new string. The outside string d gets moved into i. However, you still have to drop the const and use std::move( i ) if you want to move i into val.

Is it fine to use variables after they being used as arguments of emplace_back?

Probably, a lame question, but I keep failing to find comprehensive answer.
Parameters of std::vector::emplace_back are r-value references. As far as I understand, it is unsafe to use object after it was passed somewhere by r-value reference. I mean following:
std::string str("hello world");
std::string str2(std::move(str)); // string::string(string &&);
cout << str; // unsafe, str was moved to str2
So, what will happen in following example?
std::vector<std::string> array;
std::string str("hello world"); // what if add 'const' qualifier here?
array.emplace_back(str); // template <class... Args>
// void emplace_back (Args&&... args);
std::cout << str; // safe or not? str was moved or copied?
I'm really confused here. My tests shows that,str is safe to use after emplace_back, but my (broken?) logic tells me that str was moved and shouldn't be used after that.
PS. Sorry for my bad English :)
The parameters for emplace-style functions are forwarding references, which means they become lvalue references for lvalue arguments and rvalue references for rvalue arguments.
With
array.emplace_back(str);
str is an lvalue (you have not cast it to an rvalue with std::move) so it will be copied. It will retain its value after the call.
A standard library object will generally be in a "valid but unspecified state".
valid but unspecified state
a value of an object that is not specified except that the object’s invariants are met and operations on the
object behave as specified for its type
[Example: If an object x of type std::vector<int> is in a valid but unspecified state, x.empty() can be
called unconditionally, and x.front() can be called only if x.empty() returns false. —end example]
Most often this means either empty, or retaining the original value. Moving an int probably doesn't reset its value.
Some types are more specified, for example unique_ptr always holds a nullptr after being moved from.
So, in this case
std::string str("hello world");
std::string str2(std::move(str)); // string::string(string &&);
cout << str;
the code is valid, but we don't know exactly what the output will be, if any. Makes it less than useful.
The idea is that you should let the variable go out of scope after being moved from, or assign it a new value to use it further.
emplace_back will copy l-values, and move r-values.
One might test this with a simple example:
struct test {
test() {
std::cout << "Default-constructed\n";
}
test(test const&) {
std::cout << "Copy-constructed\n";
}
test(test &&) {
std::cout << "Move-constructed\n";
}
~test() {
std::cout << "Destructed\n";
}
test& operator=(test const&) {
std::cout << "Copy-assigned\n";
return *this;
}
test& operator=(test &&) {
std::cout << "Move-assigned\n";
return *this;
}
};
int main() {
std::vector<test> vector;
test t;
vector.emplace_back(t);//The important one
vector.emplace_back(test{});
return 0;
}
This (should, assuming copy-ellision doesn't apply here) result in the following output:
Default-constructed
Copy-constructed //The important one
Move-constructed
Destructed
Destructed
Destructed
Note that when emplace_back was called with an l-value, the copy-constructor was called. So in your case, the string would be copied, not moved, and therefore safe to keep using outside the vector.
It's also worth noting that Move-Semantics usually require that a moved-from object be "in an unspecified, but valid, state", which means it's actually not supposed to be "unsafe" to use a moved-from object. It can still have weird effects though, and can invoke undefined behavior depending on what a valid state for that object can encompass (like, for example, if you try to dereference a moved-from unique_ptr or other similar object).

move constructor and std::move confusion

I am reading about the std::move, move constructor and move assignment operator.
To be honest, all I got now is confusion. Now I have a class:
class A{
public:
int key;
int value;
A(){key = 3; value = 4;}
//Simple move constructor
A(A&& B){ A.key = std::move(B.key);
A.value = std::move(B.value);}
};
I thought B is an rvalue reference, why you can apply std::move to an ravlue reference's member?
After B.key and B.value have been moved, both have been invalidated, but how B as an object of class A gets invalidated?
What if I have A a(A()), A() is apparently an rvlaue, can A() be moved by std::move and why?
Similarly, if I have a function
int add(int && z){
int x = std:move(z);
int y = std:move(z);
return x+y;
}
What if I call add(5), how can 5 be moved and why?
And notice that z has been moved twice, after z has been moved first time, it has been invalidated, how can you move it again?
When defining foo (T && Z )(T, Z can be anything), in the body of the definition Why on earth I should use std::move(Z) since Z is already passed by an rvalue reference and when should I use std::move?
std::move does not move anything, but "marks" its argument to be a rvalue reference. Technically, it converts the type to a rvalue reference. Then, the rvalue reference it's being moved by the corresponding move constructor or move assignment operator. For objects that contain only members with trivial move ctors/assignment operators, the move ctor/assignment operator is trivial and simply copies. In general, the move ctor/assignment operator of the object calls the move ctor/assignment operator of all its members.
So, whenever you write
int x = 10;
int y = std::move(x);
on the right hand side of the assignment y = std::move(x), you have a rvalue reference of type int&&. However, int does not have a non-trivial move ctor, and the rvalue is simply copied into y, nothing is changed in x.
On the other hand,
string s = "some string";
string moved_s = std::move(s); // here we tell the compiler that we can "steal" the resource of s
is different. The move constructor of moved_s kicks in, and "steals" (i.e. swaps internal pointers etc) the resource of s, because the latter is a rvalue reference. At the end, s will not contain any element.
B is the name of an object. Once a reference has been bound, it names an object. The distinction "rvalue reference", "lvalue reference" and "named object" only applies to how the name can be bound before you got this far.
B.key is the name of a variable in the object which was supplied as argument to this function call.
"invalidated" is not part of the standard terminology for moving. Standard library objects are left in an unspecified state after being moved out of; but that's not what's going on here.
The line A.key = std::move(B.key) invokes the built-in definition of assignment for an int (this is a simple assignment, not a function call), which is just a copy. So B.key retains its value.
For A(B()) to compile, B must be a typename which you haven't defined yet. (Did you mean A(A()) ? If so, then the answer is "Yes").
See 2
Use std::move(Z.foo) whenever you want to move out of Z.foo instead of copying from Z.foo.

How to avoid unnecessary instances using rvalue references in C++

I would like to create a custom container Container that stores data in individual arrays. However, to facilitate easy iterations over the container, I provide a 'view' on the container by overloading operator[] and return a single struct Value that holds all container variables as references to the actual container. This is what I got so far:
#include <iostream>
using namespace std;
struct Value {
Value(int& data) : data_(data) { }
int& data() { return data_; }
int& data_;
};
struct Container {
Value makeValue(int i) { return Value(data_[i]); } // EDIT 1
Value&& operator[](int i) {
// return std::forward<Value>(Value(data_[i]));
return std::forward<Value>(makeValue(i)); // EDIT 1
}
int data_[5] = {1, 2, 3, 4, 5};
};
int main(int, char**)
{
// Create and output temporary
Container c;
cout << c[2].data() << endl; // Output: 3 - OK!
// Create, modify and output copy
Value v = c[2];
cout << v.data() << endl; // Output: 3 - OK!
v.data() = 8;
cout << v.data() << endl; // Output: 8 - OK!
// Create and output reference
Value&& vv = c[2];
cout << vv.data() << endl; // Output: 8 - OK, but weird:
// shouldn't this be a dangling reference?
cout << vv.data() << endl; // Output: 468319288 - Bad, but that's expected...
}
The code above is working as far as I can tell, but I'm wondering if I use the best approach here:
Is it correct to return the Value as an rvalue reference if I want to avoid unnecessary copying?
Is the use of std::forward correct? Should I use std::move (both will work in this example) or something else?
The output of the compiled program is stated in the comments. Is there any way I can avoid the dangling reference when I declare Value&& vv... (or even forbid it syntactically)?
EDIT 1
I made a small change to the source code so that the Value instance is not directly created in the operator[] method but in another helper function. Would that change anything? Should I use the makeValue(int i) method as shown or do I need to use std::move/std::forward in here?
Is it correct to return the Value as an rvalue reference if I want to avoid unnecessary copying?
No. Returning rvalue references from something that isn't a helper like std::move or std::forward is flat-out wrong. Rvalue references are still references. Returning a reference to a temporary or a local variable has always been wrong and it still is wrong. These are the same C++ rules of old.
Is the use of std::forward correct? Should I use std::move (both will work in this example) or something else?
The answer to the previous question kinda makes this one moot.
The output of the compiled program is stated in the comments. Is there any way I can avoid the dangling reference when I declare Value&& vv... (or even forbid it syntactically)?
It's not the Value&& vv = c[2]; part that creates a dangling reference. It's operator[] itself: see answer to the first question.
Rvalue references change pretty much nothing in this case. Just do things as you would have always done:
Value operator[](int i) {
return Value(data_[i]);
}
Any compiler worth using will optimise this into a direct initialisation of the return value without any copies or moves or anything. With dumb/worthless/weird/experimental compilers it will at worst involve a move (but why would anyone use such a thing for serious stuff?).
So, the line Value v = c[2]; will initialise v directly. The line Value&& vv = c[2]; will initialise a temporary and bind it to the rvalue reference variable. These have the same property as const& used to, and they extend the lifetime of the temporary to the lifetime of the reference, so it wouldn't be dangling.
In sum, the same old C++ of always still works, and still gives results that are both correct and performant. Do not forget it.
Returning a reference to a temporary objects, even if it is an r-value reference, is always wrong! By the time you access the object it will be gone. In that case it also doesn't do what you want it to do, anyway: if you want to avoid unnecessary copies, have one return statement returning a temporary! Copy/move elision will take care of the object not being copied:
Value operator[](int i) {
return Value(data_[i]);
}
Passing the temporary object through a function will inhibit copy/move elision and not copying/moving is even less work than moving.

c++ overloading operators, assignment, deep-copy and addition

I'm doing some exploration of operator-overloading at the moment whilst re-reading some of my old University text-books and I think I'm mis-understanding something, so hopefully this will be some nice easy reputation for some answerers. If this is a duplicate please point me in the right direction.
I've created a simple counter class that has (at this stage) a single member, val (an int).
I have initialised three of these counters, varOne to varThree, and want the third counter to be the sum of the first two (e.g. varThree.val is set to 5 in the below code)
counter::counter(int initialVal)
{
val = initialVal;
//pVal = new int;
//*pVal = 10; // an arbitrary number for now
}
int main (int argc, char const* argv[])
{
counter varOne(3), varTwo(2), varThree;
varThree = varOne + varTwo;
return 0;
}
I've overloaded operator+ like so:
counter operator+(counter& lhs, counter& rhs)
{
counter temp(lhs.val + rhs.val);
return temp;
}
I've made this a non-member function, and a friend of the counter class so that it can access the private values.
My problem starts when adding another private member, pVal (a pointer to an int). Adding this means that I can no longer do a simple varThree = varOne copy because when varOne is destroyed, varThree.pVal will still be pointing to the same bit of memory.
I've overloaded operator= as follows.
int counter::getN()
{
return *newVal;
}
counter& counter::operator=(counter &rhs)
{
if (this == &rhs) return *this;
val = rhs.val;
delete pVal;
pVal = new int;
*pVal = rhs.getN();
return *this;
}
Now if I do something like varThree = varOne everything copies correctly, however trying to do varThree = varOne + varTwo gives me the following error:
counter.cpp: In function ‘int main(int, const char**)’:
counter.cpp:96: error: no match for ‘operator=’ in ‘varThree = operator+(counter&, counter&)(((counter&)(& varTwo)))’
counter.cpp:55: note: candidates are: counter& counter::operator=(counter&)
make: *** [counter] Error 1
It looks as though counter::operator= is having trouble coping with the return output from operator+, and that I need to overload operator= further to accept the type that operator+ is returning, but I've had no luck and I'm beginning to think that maybe I've done something fundamentally wrong.
You need to pass your parameters as const reference. For example:
counter& counter::operator=( const counter &rhs )
And similarly for operator+(). This is necessary in order to be able to bind temporary values to the function parameter(s). Temporary values are created when you return by value, so when you say:
varOne + varTwo
a nameless temporary is created. This is the right thing to do, but you have to make sure that functions such as the assignment op can accept such values by making their parameters const.
You also need to implement the copy constructor and destructor for your class, though lack of these will not cause compilation errors (unfortunately).
Another way to approach this problem is to use the PImpl pattern and swap for the assignment operator. Assuming that you still have a counter(int) constructor you could write operator= as follows
counter& counter::operator=(const counter& rhs) {
counter temp(rhs.getN());
std::swap(&pVal,rhs.pVal);
return *this;
}
This has the benefit of leaving the messy memory management functions in the constructor and destructor where they should be.
The key here (as touched upon by a previous poster) but worth emphasizing is that expressions in C++ can be categorized as being either rvalues or lvalues.
Their is much detail behind those categories, but a useful heuristic to guide your intuition is: if you can take the address of an expression (such as a variable) it is an lvalue (there is much more to the story here, but this is a good place to start).
If it is truly not an lvalue, it is an rvalue - and a useful heuristic for rvalues is that they represent "hidden" temporary objects that the compiler instantiates to make your code work. These objects are created and destroyed by the compiler behind the scenes.
Why is this relevant here?
Well, in C++98/03 (which is what i presume you are using), remember the following two rules:
1) Only lvalue expressions can bind to non-const references (ignoring casts)
2) rvalue expressions can bind to only const references (ignoring casts)
An example will help here:
// Consider the function foo below - it returns an int -
// whenever this function is called, the compiler has
// to behave as if a temporary int object with the value 5 is returned.
// The use of 'foo()' is an expression that is an rvalue - try typing &foo() -
// [Note: if foo was declared as int& foo(), the story gets complicated, so
// i'll leave that for another post if someone asks]
int foo() { return 5; }
void bind_r(int& r) { return; }
void bind_cr(const int& cr) { return; }
int main()
{
int i = 10; // ok
int& ri = i; // ok binding lvalue to non-const reference, see rule #1
int& ri2 = foo(); // Not ok, binding a temporary (rvalue) to a non-const reference
// The temporary int is created & destroyed by compiler here
const int& cri = foo(); // ok - see rule #2, temporary int is NOT destroyed here
//Similarly
bind_r(i); // ok - rule #1
bind_r(foo()); // NOT ok - rule #2
bind_cr(foo()); // ok - rule #2
// Since the rules above keep you out of trouble, but do not exhaust all possibilities
// know that the following is well-formed too:
const int& cri2 = i;
bind_cr(i);
bind_cr(cri);
bind_cr(cri2);
}
When you bind an rvalue to a const reference, you basically extend the temporary object's life-time to the life-time (in this case scope) of the reference (and the compiler can not just destroy it at the end of that expression) - so you end up with a reference to a valid object.
I hope this helps in understanding why you have to declare your assignment operator as accepting a const reference and not just a non-const reference, as one of the other posters rightly recommended.
p.s. There are some other issues with your code (such as why you are destroying and creating the memory that your object exclusively points to upon each assignment, and the lack of a copy constructor and destructor), and if no one has addressed them by the time this post appears, i will :)
p.s. It may also be worth knowing that C++0x adds something known as rvalue references (non-const) that preferentially bind to rvalues and provide for extremely powerful optimization opportunities for the programmer (without having to rely on the optimization capabilities of the compiler) - they also help solve the problem of creating the perfect forwarding function in C++ - but now we're digressing ;)