Does this copy the vector? - c++

If I have the following code, is the vector copied?
std::vector<int> x = y.getTheVector();
or would it depend on whether the return type of getTheVector() is by reference?
or would I just need to use:
std::vector<int>& x = y.getTheVector();
or, would I need to do both?

std::vector<int> x = y.getTheVector();
always makes a copy, regardless of the return type of y.getTheVector();.
std::vector<int>& x = y.getTheVector();
would not make a copy. However, x will be valid as long as y.getTheVector() returns a reference to an object that is going to be valid after the function returns. If y.getTheVector() returns an object created in the function, x will point to an object that is no longer valid after the statement.

std::vector<int> x = y.getTheVector();
This is copy-initialization. There are three possible scenarios:
The return value of getTheVector() is a lvalue reference. In this case, the copy constructor is always invoked.
The return value of getTheVector() is a temporary. In this case, the move constructor may be called, or the move/copy may be completely elided by the compiler.
The return value is a rvalue reference (usually a terrible idea). In this case, the move constructor is called.
For this line,
std::vector<int>& x = y.getTheVector();
This only compiles if getTheVector returns a lvalue reference; a temporary cannot be bound a non-const lvalue reference. In this case, no copy is ever made; but the lifetime problem may be tricky.

std::vector<int> x = y.getTheVector();
Your first example does indeed copy the vector, regardless of whether the "getTheVector" function returns a vector or a reference to a vector.
std::vector<int>& x = y.getTheVector();
In your second example, however, you are creating a reference, so the vector will NOT be copied.

Related

Doesn't for( auto && v : arr ) clobber the contents of the array?

In these questions (also here) there is recommendation to write code like:
for( auto&& v : arr )
But doesn't moving the data from arr into v clobber the array arr? How can this style of loop be safe at all?
First, auto&& is not an rvalue reference. It is called a forwarding reference and it can either be an lvalue reference, or rvalue reference, depending on if the initializer is an lvalue or rvalue.
In this case arr is a named object, so auto&& is actually the same as auto& in this case.
Lets pretend then arr is an rvalue and auto&& is actually an rvalue reference, you still don't have to worry. A reference is just an alias to the source object, it doesn't actually do anything to the source object. No move will happen as no move operation is taking place. All you are doing is referring to the element. You'd need code in the loop like
something = std::move(v); // move is required here, even though v is an rvalue reference
// because since it has a name, it is an lvalue
in order to move what v is referring to.
With the loop as is, the biggest concern is that you can change the values stored in arr since v has non-const access to arr. Using
for (const auto& v : arr)
lets you still accept lvalues and rvalues, but also does not allow you to modify the elements.

non-trivial rvalue and lvalue example

I am not sure about some rvalue/lvalue non-trvial examples.
are std::vector<int>({1,2,3})[0] and std::vector<int>() expressions below lvalue or rvalue?
the code has no actual usage but it surprises me a bit that the code is valid. It looks like they are both lvalues. or not?
#include <vector>
int main()
{
std::vector<int>({1,2,3})[0] = 0;
std::vector<int>() = {1,2,3};
return 0;
}
further example...
The std::vector<int>() expression below is a rvalue. right?
and how about the expression std::vector<int>({1,2,3})[0]?
Both the vector object from std::vector<int>() and the int value from ...[0] are temporary values. right?
auto v = std::vector<int>();
int i = std::vector<int>({1,2,3})[0];
std::vector<int>() is an rvalue, because the syntax type() always produces an rvalue. std::vector<int>() = {1,2,3}; is allowed because assigning to rvalues of class type is allowed in general, but can be disabled for a specific class by defining operator= with a & qualifier, which std::vector doesn't do.
std::vector<int>({1,2,3})[0] is an lvalue, because std::vector<...>::operator[] is defined to return an lvalue reference. It's possible to define it to return rvalue or lvalue depending on whether the vector it's called on an rvalue or lvalue (see the link above), but std::vector doesn't do that.
Both the vector object from std::vector() and the int value from ...[0] are temporary values. right?
The vector - yes. The int - no, at least formally.
Whether or not something an object is a temporary depends solely on the way it was created, e.g. type() will always give you a temporary.
The vector allocates its elements on the heap, which results in normal, non-temporary objects. It doesn't matter that the vector itself is a temporary.

Which variant of operator [] of std::vector gets called when the following line is executed and why?

According to C++ Reference Operator[] of std::vector has 2 variants
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;
When we execute the following line which of the above variant gets called?
std::vector<int> vlist;
vlist[0] = 7;
My thought, the 1st variant should not be called as I am assigning rvalue to an non-const lvalue reference, the way int& x = 7; is illegal.
Because vlist is non const, the first option (the non-const one) will get called.
The compiler looks for the most correct method to call.
Only const methods can be called on const objects. In case you have a non-const object, both const and non-const methods of the object can be called.
If there are both const and non-const implementations of a method, the correct one will be called, depending on the constness of the object itself.
Your example of int& x = 7 is indeed illegal but it's not the case presented above (vlist[0] = 7). You are right that one cannot declare a reference to an rvalue. But assigning an rvalue to a reference object is perfectly fine.
The first variant is called. int &x = 7; is not legal because this is a definition. But int &x = y; x = 7; is perfectly fine. And here you get already constructed reference so assignment is OK. Also assigning const to non-const is not a problem, because you can create non-const copy of const;

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.

Reference to element of vector returned by a function in C++

Can someone verify that the following is a BUG, and explain why? I think I know, but am unclear about the details. (My actual problem involved a vector of enums, not ints, but I don't think it should matter.) Suppose I have the following code:
std::vector<int> f (void) {
std::vector<int> v;
v.push_back(5);
return v;
}
void g (void) {
const int &myIntRef = f()[0];
std::cout << myIntRef << std::endl;
}
Am I correct that myIntRef is immediately a dangling reference, because the return value of f is saved nowhere on the stack?
Also, is the following a valid fix, or is it still a bug?
const int myIntCopy = f()[0]; // copy, not a reference
In other words, is the return result of f() thrown away before the 0th element can be copied?
That is a bug. At the end of the complete expression const int &myIntRef = f()[0]; the temporary vector will be destroyed and the memory released. Any later use of myIntRef is undefined behavior.
Under some circumstances, binding a reference to a temporary can extend the lifetime of the temporary. This is not one of such cases, the compiler does not know whether the reference returned by std::vector<int>::operator[] is part of the temporary or a reference to an int with static storage duration or any other thing, and it won't extend the lifetime.
Yes, it is wrong thing to do indeed. When you call:
return v;
temporary copy of object v is being created and
const int &myIntRef = f()[0];
initializes your reference with the first element of this temporary copy. After this line, the temporary copy no longer exists, meaning that myIntRef is an invalid reference, using of which produces undefined behavior.
What you should do is:
std::vector<int> myVector = f();
const int &myIntRef = myVector[0];
std::cout << myIntRef << std::endl;
which (thanks to copy elision) uses an assignment operator to initialize myVector object by using v without copy of v being created. In this case the lifetime of your reference is equal to the lifetime of myVector object, making it perfectly valid code.
And to your second question:
"Also, is the following a valid fix, or is it still a bug?"
const int myIntCopy = f()[0]; // copy, not a reference
Yes, this is another possible solution. f()[0] will access the first element of the temporary copy and use its value to initialize myIntCopy variable. It is guaranteed that the copy of v returned by f() exists at least until the whole expression is executed, see C++03 Standard 12.2 Temporary objects ยง3:
Temporary objects are destroyed as the last step in evaluating the full-expression (1.9) that (lexically) contains the point where they were created.