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.
Related
Can someone tell me if this is safe, because I think it isn't:
class A
{
public:
A(int*& i) : m_i(i)
{}
int*& m_i;
};
class B
{
public:
B(int* const& i) : m_i(i)
{}
int* const & m_i;
};
int main()
{
int i = 1;
int *j = &i;
A a1(j); // this works (1)
A a2(&i); // compiler error (2)
B b(&i); // this works again (3)
}
I understand why (1) works. We are passing a pointer, the function accepts it as a reference.
But why doesn't (2) work? From my perspective, we are passing the same pointer, just without assigning it to a pointer variable first. My guess is that &i is an rvalue and has no memory of its own, so the reference cannot be valid. I can accept that explanation (if it's true).
But why the heck does (3) compile? Wouldn't that mean that we allow the invalid reference so b.m_i is essentially undefined?
Am I completely wrong in how this works? I am asking because I am getting weird unit test fails that I can only explain by pointers becoming invalid. They only happen for some compilers, so I was assuming this must be something outside the standard.
So my core question basically is: Is using int* const & in a function argument inherently dangerous and should be avoided, since an unsuspecting caller might always call it with &i like with a regular pointer argument?
Addendum: As #franji1 pointed out, the following is an interesting thought to understand what happens here. I modified main() to change the inner pointer and then print the members m_i:
int main()
{
int i = 1;
int *j = &i; // j points to 1
A a1(j);
B b(&i);
int re = 2;
j = &re; // j now points to 2
std::cout << *a1.m_i << "\n"; // output: 2
std::cout << *b.m_i << "\n"; // output: 1
}
So, clearly a1 works as intended.
However, since b cannot know that j has been modified, it seems to hold a reference to a "personal" pointer, but my worry is that it is not well defined in the standard, so there might be compilers for which this "personal" pointer is undefined. Can anyone confirm this?
A's constructor takes a non-const reference to an int* pointer. A a1(j); works, because j is an int* variable, so the reference is satisfied. And j outlives a1, so the A::m_i member is safe to use for the lifetime of a1.
A a2(&i); fails to compile, because although &i is an int*, operator& returns a temporary value, which cannot be bound to a non-const reference.
B b(&i); compiles, because B's constructor takes a reference to a const int*, which can be bound to a temporary. The temporary's lifetime will be extended by being bound to the constructor's i parameter, but will then expire once the constructor exits, thus the B::m_i member will be a dangling reference and not be safe to use at all after the constructor has exited.
j is an lvalue and as such it can be bound to a non-const lvaue reference.
&i is a prvalue and it cannot be bound to non-const lvalue reference. That's why (2) doesn't compile
&i is a prvalue (a temporary) and it can be bound to a const lvalue reference. Bounding a prvalue to a reference extends the lifetime of the temporary to the lifetime of the reference. In this case this temporary lifetime is extended to the lifetime of the constructor parameter i. You then initialize the reference m_i to i (constructor parameter) (which is a reference to the temporary) but because i is an lvalue the lifetime of the temporary is not extended. In the end you end up with a reference member m_i bound to an object which is not alive. You have a dangling reference. Accessing m_i from now on (after the constructor has finished) is Undefined Behavior.
Simple table of what can references bind to: C++11 rvalue reference vs const reference
Pointer is a memory address. For simplicity, think of a pointer as uint64_t variable holding a number representing the memory address of whatever. Reference is just a alias for some variable.
In example (1) you are passing a pointer to constructor expecting a reference to pointer. It works as intended, as compiler gets the address of memory where the value of pointer is stored and passes it to constructor. The constructor gets that number and creates an alias pointer. As a result you are getting an alias of j. If you modify j to point to something else then m_i will also be modified. You can modify m_i to point to something else too.
In example (2) you are passing a number value to the constructor expecting a reference to pointer. So, instead of an address of an address, constructor gets an address and compiler has no way to satisfy the signature of the constructor.
In example (3) you are passing a number value to constructor expecting a constant reference to pointer. Constant reference is a fixed number, just a memory address. In this case compiler understands the intent and provides the memory address to set in the constructor. As a result you are getting fixed alias of i.
EDIT (for clarity): Difference between (2) and (3) is that &i is not a valid reference to int*, but it is a valid const reference to int*.
When a function parameter is of type lvalue reference lref:
void PrintAddress(const std::string& lref) {
std::cout << &lref << std::endl;
}
and lref is bound to a prvalue:
PrintAddress(lref.substr() /* temporary of type std::string */)
what does the address represent? What lives there?
A prvalue cannot have its address taken. But an lvalue reference to a prvalue can have its address taken, which is curious to me.
Inside the function lref is not a prvalue it is an lvalue and you can take the address of it.
There is a common misconception about rvalues vs. lvalues.
A named parameter is always an lvalue. No matter whether it is a reference type that is bound to an rvalue. Through a const & reference type you can't even tell which kind of value category the object actually has at the point where the function is called. Rvalue references and non-const Lvalue references give you that information:
void foo(std::string& L, std::string&& R)
{
// yeah i know L is already an lvalue at the point where foo is called
// R on the other hand is an rvalue at the point where we get called
// so we can 'safely' move from it or something...
}
The temporary string is a prvalue in the context of the caller (at the point PrintAddress is called). Within the context of the callee (in PrintAddress) lref is an lvalue reference because in this context it actually is an lvalue.
PrintAddress isn't aware of the limited lifetime of the passed argument and from PrintAddress' point of view the object is "always" there.
std::string q("abcd");
PrintAddress(q.substr(1)); // print address of temporary
is conceptually equivalent to:
std::string q("abcd");
{
const std::string& lref = q.substr(1);
std::cout << &lref << std::endl;
}
where the temporary experiences a prolongation of its lifetime to the end of the scope in which lref is defined (which is to the end of PrintAddress function scope in the present example).
what does the address represent? What lives there?
A std::string object containing the passed content.
And is it legal (in C++, and with respect to memory) to write to that address?
No, it would be legal if you'd use an rvalue reference:
void PrintAddressR(std::string&& rref) {
rref += "Hello"; // writing possible
std::cout << &rref << std::endl; // taking the address possible
}
// ...
PrintAddressR(q.substr(1)); // yep, can do that...
The same applies here: rref is an lvalue (it has a name) so you can take its address plus it is mutable.
In short, because the prvalue's lifetime has been extended. By having its lifetime extended - by any reference -, it's an lvalue, and thus can have its address taken.
what does the address represent? What lives there?
The address represents an object, the object referenced by lref.
A prvalue is short lived, it doesn't live for long. In fact, it will be destroyed when the statement creating it ends.
But, when you create a reference to a prvalue (either an rvalue reference or a const lvalue reference), its lifetime is extended. Ref.::
An rvalue may be used to initialize a const lvalue [rvalue] reference, in which case the lifetime of the object identified by the rvalue is extended until the scope of the reference ends.
Now it makes actually sense to take its address, as it is an lvalue for all intents and purposes. Now, that the prvalue has an indeterminate lifetime, it is an lvalue.
Taking the address of a prvalue doesn't make sense however, and that's probably why it is disallowed:
The value is destroyed after the next statements, so you can't do anything with the address, except maybe print it out.
If you take the address of something, the compiler is required to actually create the object. Sometimes, the compiler will optimize out variables that are trivial, but if you were to take the address of them, the compiler won't be allowed to optimize them out.
Taking the address of a prvalue will thus result in the compiler being unable to elide the value completely, for no advantages whatsoever (see point 1).
In simple English:
void PrintAddress(const std::string& lref) {
std::cout << &lref << std::endl;
}
Any object that has a name is an lvalue, hence any use of lref within the scope of the funtion above is an lvalue use.
When you called the function with:
PrintAddress(lref.substr() /* temporary of type std::string */)
Of cause, lref.substr() produces a temporary which is an rvalue, but rvalues can bind to (have its lifetime extended by) const lvalue references or rvalue references.
Even if you provided an rvalue overload, for the fact it has a name, its an "lvalue of something" within its scope, example:
#include <string>
#include <iostream>
void PrintAddress(const std::string& lref) {
std::cout << "LValue: " << &lref << std::endl;
}
void PrintAddress(std::string&& `rref`) {
std::cout << "RValue: " << &rref << std::endl; //You can take address of `rref`
}
int main(){
std::string str = "Hahaha";
PrintAddress(str);
PrintAddress(str.substr(2));
}
Just remember:
In C++, any object(whether value type, reference type or pointer type) that has a name is an lvalue
Also know that some expressions produce lvalues too.
Temporary objects are sometimes created in C++. One example from the STL valarray class would be the slice_array, which is invoked every time a series of indices of a valarray is selected.
http://www.cplusplus.com/reference/valarray/slice_array/
My question is this:
When passing these temporary objects as arguments to a function, is a copy of these objects passed, or only a reference?
E.g. imagine these two naive functions:
double simple_product(double* inp,int length){
double res=1;
for(int i=0;i<length;++i){
res = res*inp[i];
}
return(res);
}
double sum_selected(valarray<double> v){
simple_product(&v[0],v.size());
return(v.sum());
}
If I call them in the following fashion:
valarray<double> valery(10,10);
size_t sel[] = {1,3,4};
valarray<size_t> selection (sel,3);
cout << sum_selected(valery[selection]);
will a new object with a size of 3*size_t be temporarily created within the stack of the function sum_selected or not?
Please note that declaring the function as: double sum_selected(valarray<double> & v)
is not permitted (temporary objects can only be bound to const references).
The reason why this is interesting is that, for example here, it is not possible to declare the function as:
double sum_selected(const valarray<double> & v), because then the function simple_product (which is to be assumed unalterable) cannot be called. However, making a temporary copy of the passed argument would be problematic for memory in case of big arrays.
If the function is declared to take its argument by value, then it's passed by value, creating a new copy of the object:
void f(thing t); // pass by value
If it's declared to take its argument by reference, then it's passed by reference. But it can only take a temporary by const or rvalue reference:
void f(thing const & t); // OK: const lvalue reference
void f(thing && t); // OK: rvalue reference
void f(thing & t); // Error: lvalue reference can't bind to temporary
Passing by reference doesn't create a new object, and the lifetime of the temporary is such that it's valid during the function call, until the end of the statement that creates it.
valarray<T>::operator[](valarray<size_t> const&) returns an indirect_array<T> (reference), or a valarray<T> for the const-qualified operator, not a slice_array.
If you want to be able to access the selected elements as a contiguous array, then you'll need to collect them into a contiguous array. The converting constructor valarray<T>::valarray(indirect_array<T> const&) does this for you. Reference semantics would be useless in this case, as there is no existing object that has your desired elements arranged contiguously.
Changing the function signature to double sum_selected(valarray<double> const&) would make no difference to your code, as a temporary valarray<double> is constructed anyway. It would be more efficient in the case where valery is passed directly, without subscripting.
This code does compile but I'm just beginning studying C++11 and I can't understand what's happening here behind the scenes.
void func(int&& var)
{
int var2(var);
}
void main()
{
int var1 = 22;
func(move(var1));
}
My guess: move(var1) returns a r-value of var1 (probably its data) and the func function is initializing var2 with the r-value of var1. But why is var1 still valid after the func call? Shouldn't it have an invalid value because its temp value has been re-assigned to var2?
There are several issues here.
One is that you're working with an int. And for an int a copy is just as fast as a move. So it's perfectly reasonable for a move to be implemented as a copy.
There is no requirement that move construction (or assignment) alter the value of the thing being moved from. It's a logical error in your code to continue to treat it as if it had a useful value, but it's not required that the value become useless.
The second is that your code as written doesn't actually move anything. Simply using ::std::move does not result in something being moved. It's just a nice way to turn an lvalue into an rvalue so that something can be moved. In your case, var has a name, so it's an lvalue. Your initialization of var2 in func is actually a copy, not a move. If you had written it int var2(move(var)); it would then be a move and var1 in main would be potentially invalidated at that point.
To reiterate, the ::std::move function is just there to signal to people reading your code that a move may happen and that the value of the variable after that cannot be counted on. It doesn't actually move anything.
Here is a marked up version of your code:
// func requires a temporary argument, something that can be moved from
void func(int&& var)
{
int var2(var); // Doesn't actually call int(int &&), calls int(int &)
// int var2(move(var)); // Would actually call int(int &&) and a move would
// happen then at this point and potentially alter the
// value of what var is referencing (which is var1
// in this code).
}
void main()
{
int var1 = 22;
func(move(var1)); // Tell people that var1's value may be unspecified after
// Also, turn var1 into a temporary to satisfy func's
// signature.
}
Since your code, as written, does not result in any moves happening, here is a version that does definitely move something somewhere:
#include <vector>
#include <utility>
using ::std;
// Still require an rvalue (aka a temporary) argument.
void func(vector<int>&& var)
{
// But, of course, inside the function var has a name, and is thus an lvalue.
// So use ::std::move again to turn it into an rvalue.
vector<int> var2(move(var));
// Poof, now whetever var was referencing no longer has the value it used to.
// Whatever value it had has been moved into var2.
}
int main()
{
vector<int> var1 = { 32, 23, 66, 12 };
func(move(var1)); // After this call, var1's value may no longer be useful.
// And, in fact, with this code, it will likely end up being an empty
// vector<int>.
}
And, of course, this way of writing it is silly. There are sometimes reasons to specify that an argument be a temporary. And that's usually if you have one version of a function that takes a reference, and another that takes an rvalue reference. But generally you don't do that. So, here's the idiomatic way to write this code:
#include <vector>
#include <utility>
using ::std;
// You just want a local variable that you can mess with without altering the
// value in the caller. If the caller wants to move, that should be the caller's
// choice, not yours.
void func(vector<int> var)
{
// You could create var2 here and move var into it, but since var is a
// local variable already, why bother?
}
int main()
{
vector<int> var1 = { 32, 23, 66, 12 };
func(move(var1)); // The move now does actually happen right here when
// constructing the argument to func and var1's value
// will change.
}
Of course, giving a name to var1 in that code is kind of silly. Really, it should just be written this way:
func(vector<int>{ {32, 23, 66, 12} });
Then you're just constructing a temporary vector right there and passing it into func. No explicit use of move required.
There is nothing to be moved from an int hence a copy is created. For the types that have move constructor or move assignment operator has specific purpose of moving the underlying resources, then there is a change other wise it is a copy.
The std::move will cast an lvalue to a rvalue so that some resources(objects on heap or file handles) can be moved out to other object however the actual movement happens in move constructor or move assignment operator.
For example consider vector in standard library which has move copy constructor and move assignment operator that does some explicit work to move resources across.
Having said this I believe that the way the type is moved inside function funcis not proper way to attempt a move. int&& var is the reference to rvalue (casted with std::move) however var (by name) is like a lvalue and int var2(var); would anyway not move the var to var2 and it will be COPY. The correct way to attempt a move would be int var2(std::move(var)); I mean if a type has move constructor which can move the resources you will have to use like that.
void func(int&& var) //2. Var is a reference to rvalue however the by name the var is a lvalue and if passed as it would invoke copy constructor.
{
int var2(std::move(var)); // 3. hence to invoke move constructor if it exists the correct attempt would be to case it to rvalue again as move constructors take rvalue. If the move constructor does not exists then copy is called.
}
void main()
{
int var1 = 22;
func(move(var1)); //1. Cast the type to rvalue so that it can be passed to a move copy contructor or move assigment operator.
}
The expression var1 is an lvalue. Applying std::move(var1) gives you an rvalue referring to the same object. You then bind this rvalue to the int&& called var.
The expression var is also an lvalue. This is because any expression which is a named variable is an lvalue (even if its type is an rvalue reference). You then initialise var2 with the value of var.
So all you've done is copied the value from var1 to var2. Nothing has been moved at all. In fact, you cannot even move from a fundamental type like an int. Attempting to initialise or assign from a temporary int will just copy its value and not move anything.
Even if you were using a type T that had a move constructor and assignment operator, your code wouldn't move anything. That's because the only non-reference initialisation you do is the line int var2(var);, but here var is an lvalue. That means the copy constructor will be used instead.
The simplest way to move from var1 to var2 would be to do this:
void func(T var)
{
T var2(move(var));
}
void main()
{
T var1(22);
func(move(var1));
}
This will move from var1 to create var and then it will move from var to create var2.
You could do this in almost the same way, except change var back to an rvalue reference, but I don't recommend it. You would have to document that the rvalue being passed will be invalidated by the internal moving.
move(var1) returns a r-value of var1 (probably its data)
No, move(var1) returns an rvalue reference, referring to var1.
and the func function is initializing var2 with the r-value of var1
func copies var1 to var2. If var1 were of a type with a move constructor, then the move constructor would be called to initialize var2. But it is not.
But why is var1 still valid after the func call?
Because copying an int doesn't make it invalid.
Shouldn't it have an invalid value?
There's no special "invalid value" of int. Using an uninitialized object has undefined behavior, but this object is not uninitialized.
As others have stated, your example uses a primitive type, which in essence invokes a copy, because a move just simply cannot occur here. However, I've created an example where a move can occur, using std::string. Its guts can be moved out from under, and you can see in this example that is what happens.
#include<iostream>
#include<string>
#include<utility>
void func(std::string&& var)
{
// var is an lvalue, so call std::move on it to make it an rvalue to invoke move ctor
std::string var2(std::move(var));
std::cout << "var2: " << var2 << std::endl;
}
int main()
{
std::string var1 = "Tony";
std::cout << "before call: " << var1 << std::endl;
func(std::move(var1));
std::cout << "after call: " << var1 << std::endl;
}
Output:
before call: Tony
var2: Tony
after call:
You can see that var1 has indeed been moved from and it no longer contains any data, however it is only in an unspecified state per the Standard and is still reusable.
Live Example
I tried following code :
#include<iostream>
#include<string>
using namespace std;
string f1(string s)
{
return s="f1 called";
}
void f2(string *s)
{
cout<<*s<<endl;
}
int main()
{
string str;
f2(&f1(str));
}
But this code doesn't compile.
What I think is : f1 returns by value so it creates temporary, of which I am taking address and passing to f2.
Now Please explain me where I am thinking wrong?
The unary & takes an lvalue (or a function name). Function f1() doesn't return an lvalue, it returns an rvalue (for a function that returns something, unless it returns a reference, its return value is an rvalue), so the unary & can't be applied to it.
It is possible to create (and pass) a pointer to a temporary object, assuming that you know what you are doing. However, it should be done differently.
A function with return value of non-reference type returns an rvalue. In C++ applying the built-in unary operator & to an rvalue is prohibited. It requires an lvalue.
This means, that if you want to obtain a pointer to your temporary object, you have to do it in some other way. For example, as a two-line sequence
const string &r = f1(str);
f2(&r);
which can also be folded into a single line by using a cast
f2(&(const string &) f1(str));
In both cases above the f2 function should accept a const string * parameter. Just a string * as in your case won't work, unless you cast away constness from the argument (which, BTW, will make the whole thing even uglier than it already is). Although, if memory serves me, in both cases there's no guarantee that the reference is attached to the original temporary and not to a copy.
Just keep in mind though creating pointers to temporary objects is a rather dubious practice because if the obvious lifetime issues. Normally you should avoid the need to do that.
Your program doesn't compile because f1 has a parameter and you're not passing any.
Additionally, the value returned from a function is an rvalue, you can't take its address.
Try this:
int main()
{
string str;
string str2 = f1(str); // copy the temporary
f2(&str2);
}