I'm using the embedded templated library with etl::queue
https://www.etlcpp.com/queue.html
the etl::queue is quitvalent to std::queue
In order to avoid copying I'd like to actually move the element into the queue.
Now my setup looks like this
bool CETLConcurrentQueue<ElementType_T, u32QueueSize>::Add(ElementType_T &&element, const uint32_t u32Timeout)
{
//lock mutex...
queue.push(element);
//do further stuff
}
Now I'm not using queue.push(std::move(element)); because element is allready an rvalue Reference
However, queue.push(element); calls elements copy constructor (which is deleted)
How can I call elements move constructor instead?
You have to use std::move to convert element to rvalue. As a named variable element itself is an lvalue, even its type is an rvalue reference.
queue.push(std::move(element));
Note that types and value categories are two independent things.
(emphasis mine)
Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category.
...
The following expressions are lvalue expressions:
the name of a variable, a function, a template parameter object (since C++20), or a data member, regardless of type, such as std::cin
or std::endl. Even if the variable's type is rvalue reference, the
expression consisting of its name is an lvalue expression;
Related
I was reading a book about data structure implemented in C++, I dont understand a code snippet, it's part of vector class
void push_back(object &&x) {
//do something
objects[size++] = std::move(x);
}
I know that std::move return a rvalue reference of the object, but the push_back member function already has rvalue reference x as parameter, isn't the std::move here unnecessary?
Another question is if we have a rvalue reference of a class object, we still need to use std::move on its member if we want to call move instead of copy right? like the code below:
A& operator=(A&& other) {
member = std::move(other.member);
return *this;
}
isn't the std::move here unnecessary?
No. Types and value categories are different things.
(emphasis mine)
Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category.
The following expressions are lvalue expressions:
the name of a variable, a function, a template parameter object (since
C++20), or a data member, regardless of type, such as std::cin or
std::endl. Even if the variable's type is rvalue reference, the
expression consisting of its name is an lvalue expression;
std::move converts lvalue to rvalue (xvalue). As a named variable, x is an lvalue, std::move converts it to rvalue in objects[size++] = std::move(x); then the move assignment operator is supposed to be used. Otherwise, copy assignment operator will be used instead; lvalue can't be bound to rvalue reference.
we still need to use std::move on its member if we want to call move instead of copy right?
Yes, same reason as above.
x has a name, thus it's an lvalue inside the function. The rvalue reference was bound to the lvalue x. std::move casts it back to the rvalue that was passed in.
I think there's something I'm not quite understanding about rvalue references. Why does the following fail to compile (VS2012) with the error 'foo' : cannot convert parameter 1 from 'int' to 'int &&'?
void foo(int &&) {}
void bar(int &&x) { foo(x); };
I would have assumed that the type int && would be preserved when passed from bar into foo. Why does it get transformed into int once inside the function body?
I know the answer is to use std::forward:
void bar(int &&x) { foo(std::forward<int>(x)); }
so maybe I just don't have a clear grasp on why. (Also, why not std::move?)
I always remember lvalue as a value that has a name or can be addressed. Since x has a name, it is passed as an lvalue. The purpose of reference to rvalue is to allow the function to completely clobber value in any way it sees fit. If we pass x by reference as in your example, then we have no way of knowing if is safe to do this:
void foo(int &&) {}
void bar(int &&x) {
foo(x);
x.DoSomething(); // what could x be?
};
Doing foo(std::move(x)); is explicitly telling the compiler that you are done with x and no longer need it. Without that move, bad things could happen to existing code. The std::move is a safeguard.
std::forward is used for perfect forwarding in templates.
Why does it get transformed into int once inside the function body?
It doesn't; it's still a reference to an rvalue.
When a name appears in an expression, it's an lvalue - even if it happens to be a reference to an rvalue. It can be converted into an rvalue if the expression requires that (i.e. if its value is needed); but it can't be bound to an rvalue reference.
So as you say, in order to bind it to another rvalue reference, you have to explicitly convert it to an unnamed rvalue. std::forward and std::move are convenient ways to do that.
Also, why not std::move?
Why not indeed? That would make more sense than std::forward, which is intended for templates that don't know whether the argument is a reference.
It's the "no name rule". Inside bar, x has a name ... x. So it's now an lvalue. Passing something to a function as an rvalue reference doesn't make it an rvalue inside the function.
If you don't see why it must be this way, ask yourself -- what is x after foo returns? (Remember, foo is free to move x.)
rvalue and lvalue are categories of expressions.
rvalue reference and lvalue reference are categories of references.
Inside a declaration, T x&& = <initializer expression>, the variable x has type T&&, and it can be bound to an expression (the ) which is an rvalue expression. Thus, T&& has been named rvalue reference type, because it refers to an rvalue expression.
Inside a declaration, T x& = <initializer expression>, the variable x has type T&, and it can be bound to an expression (the ) which is an lvalue expression (++). Thus, T& has been named lvalue reference type, because it can refer to an lvalue expression.
It is important then, in C++, to make a difference between the naming of an entity, that appears inside a declaration, and when this name appears inside an expression.
When a name appears inside an expression as in foo(x), the name x alone is an expression, called an id-expression. By definition, and id-expression is always an lvalue expression and an lvalue expressions can not be bound to an rvalue reference.
When talking about rvalue references it's important to distinguish between two key unrelated steps in the lifetime of a reference - binding and value semantics.
Binding here refers to the exact way a value is matched to the parameter type when calling a function.
For example, if you have the function overloads:
void foo(int a) {}
void foo(int&& a) {}
Then when calling foo(x), the act of selecting the proper overload involves binding the value x to the parameter of foo.
rvalue references are only about binding semantics.
Inside the bodies of both foo functions the variable a acts as a regular lvalue. That is, if we rewrite the second function like this:
void foo(int&& a) {
foo(a);
}
then intuitively this should result in a stack overflow. But it doesn't - rvalue references are all about binding and never about value semantics. Since a is a regular lvalue inside the function body, then the first overload foo(int) will be called at that point and no stack overflow occurs. A stack overflow would only occur if we explicitly change the value type of a, e.g. by using std::move:
void foo(int&& a) {
foo(std::move(a));
}
At this point a stack overflow will occur because of the changed value semantics.
This is in my opinion the most confusing feature of rvalue references - that the type works differently during and after binding. It's an rvalue reference when binding but it acts like an lvalue reference after that. In all respects a variable of type rvalue reference acts like a variable of type lvalue reference after binding is done.
The only difference between an lvalue and an rvalue reference comes when binding - if there is both an lvalue and rvalue overload available, then temporary objects (or rather xvalues - eXpiring values) will be preferentially bound to rvalue references:
void goo(const int& x) {}
void goo(int&& x) {}
goo(5); // this will call goo(int&&) because 5 is an xvalue
That's the only difference. Technically there is nothing stopping you from using rvalue references like lvalue references, other than convention:
void doit(int&& x) {
x = 123;
}
int a;
doit(std::move(a));
std::cout << a; // totally valid, prints 123, but please don't do that
And the keyword here is "convention". Since rvalue references preferentially bind to temporary objects, then it's reasonable to assume that you can gut the temporary object, i.e. move away all of its data away from it, because after the call it's not accessible in any way and is going to be destroyed anyway:
std::vector<std::string> strings;
string.push_back(std::string("abc"));
In the above snippet the temporary object std::string("abc") cannot be used in any way after the statement in which it appears, because it's not bound to any variable. Therefore push_back is allowed to move away its contents instead of copying it and therefore save an extra allocation and deallocation.
That is, unless you use std::move:
std::vector<std::string> strings;
std::string mystr("abc");
string.push_back(std::move(mystr));
Now the object mystr is still accessible after the call to push_back, but push_back doesn't know this - it's still assuming that it's allowed to gut the object, because it's passed in as an rvalue reference. This is why the behavior of std::move() is one of convention and also why std::move() by itself doesn't actually do anything - in particular it doesn't do any movement. It just marks its argument as "ready to get gutted".
The final point is: rvalue references are only useful when used in tandem with lvalue references. There is no case where an rvalue argument is useful by itself (exaggerating here).
Say you have a function accepting a string:
void foo(std::string);
If the function is going to simply inspect the string and not make a copy of it, then use const&:
void foo(const std::string&);
This always avoids a copy when calling the function.
If the function is going to modify or store a copy of the string, then use pass-by-value:
void foo(std::string s);
In this case you'll receive a copy if the caller passes an lvalue and temporary objects will be constructed in-place, avoiding a copy. Then use std::move(s) if you want to store the value of s, e.g. in a member variable. Note that this will work efficiently even if the caller passes an rvalue reference, that is foo(std::move(mystring)); because std::string provides a move constructor.
Using an rvalue here is a poor choice:
void foo(std::string&&)
because it places the burden of preparing the object on the caller. In particular if the caller wants to pass a copy of a string to this function, they have to do that explicitly;
std::string s;
foo(s); // XXX: doesn't compile
foo(std::string(s)); // have to create copy manually
And if you want to pass a mutable reference to a variable, just use a regular lvalue reference:
void foo(std::string&);
Using rvalue references in this case is technically possible, but semantically improper and totally confusing.
The only, only place where an rvalue reference makes sense is in a move constructor or move assignment operator. In any other situation pass-by-value or lvalue references are usually the right choice and avoid a lot of confusion.
Note: do not confuse rvalue references with forwarding references that look exactly the same but work totally differently, as in:
template <class T>
void foo(T&& t) {
}
In the above example t looks like a rvalue reference parameter, but is actually a forwarding reference (because of the template type), which is an entirely different can of worms.
Consider the following case
struct A {
operator int();
};
int &&x = A();
The spec says at http://eel.is/c++draft/dcl.init.ref#5 about whether the reference binding is direct or indirect
In all cases except the last (i.e., creating and initializing a temporary from the initializer expression), the reference is said to bind directly to the initializer expression.
The case above doesn't match the last, but the second last bullet.
If T1 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered ... The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference.
Therefore the binding of A() to the reference is indirect binding. To carry on, we will recurse into reference initialization again, now with a prvalue of type int, trying to initialize int&&. Now we will end up with the last bullet, which means direct binding.
So what can we say about the binding of the reference... does it bind directly or indirectly? Does it do both, depending on what expression you consider (initializer expression vs result of the conversion function call)?
In our case in particular, the paragraphs seem to say we bind directly to the initializer expression and bind indirectly to the result of the conversion on the initializer expression. However, in the chapter on overload resulution http://eel.is/c++draft/over.ics.ref, we only distinguish between
When a parameter of reference type binds directly ([dcl.init.ref]) to an argument expression,
If the parameter binds directly to the result of applying a conversion function to the argument expression,
When a parameter of reference type is not bound directly to an argument expression,
For a case like
void f(int &&);
f(A());
Case 1 applies, but I'm pretty sure it is not intended to apply. My gut feeling is that "In all cases except the last" is not only intended to match the creation-of-temporary case, but also the second last bullet or alternatively to the whole "Otherwise:" branch that contains the two bullets (i.e the "last" refers to a different level on the bullet-hierarchy). Can you please clarify?
While not the actual standard, I am relying this page on cppreference.com for this specific verbiage:
An lvalue is an expression that identifies a non-temporary object or a
non-member function.
The following expressions are lvalues:
The name of a variable or function in scope, regardless of type, such as std::cin or std::endl. Even if the variable's type is rvalue
reference, the expression consisting of its name is an lvalue
expression.
...
My (simplified) comprehension of the quoted section above is that an lvalue:
Is a reference
Must not be a temporary object
I know that references are not objects, so point #2 must mean that in terms of a reference, it must not refer to a temporary object. However, from the expression itself, does that mean a reference to a temporary is not an lvalue? You can have references to temporary and non-temporary objects:
int myvar = 0;
int& ref_myvar = myvar; // Reference to non-temporary
class foo {};
foo const& ref_foo = foo{}; // Reference to temporary
In the above code snippet, usage of ref_foo in a separate expression later would be an lvalue or not? There is a rule for rvalue references that states that rvalue references used by name in an expression are still lvalues (because you refer to the name at that point). Does this rule also apply to lvalue references (since they also have names and using the name itself in an expression would make it an lvalue, as it does for rvalue reference variables)?
I hope I'm making some sort of sense. At least I hope the source of my confusion is evident. Some examples of how the lvalue references above would be used in an expression to prove some points relevant to my question would be a huge help as well.
I'd say that the cppreference wording is OK for a "general introduction"-level discussion or even for "most everyday uses"-level discussion. However, once you get into the fine technical details, such statements can become somewhat misleading.
The important point is that the value category ("being an lvalue") is the property of an expression, not of an object. You can have a temporary object accessed through an lvalue, and you can have a non-temporary object accessed through an rvalue.
To refer to your examples:
ref_myvar and ref_foo are both lvalues, and always will be, regardless of how you use them. In the following:
foo&& rref = foo{};
rref is, and always will be, an lvalue as well. It is a reference to an rvalue, but the reference itself has a name and so is an lvalue.
If you want to treat an lvalue as an rvalue, you use the standard-provided case operator for that:
rvalue = std::move(lvalue);
Let's analyse this code:
int someint = std::move(ref_myvar);
ref_myvar is an lvalue. std::move(ref_myvar) is an rvalue. someint is an lvalue.
I don't think there is a concise way to define an lvalue without going full standardese, but name (or absence thereof) plays an important part in most definitions. I'll try my hand at such a definition; these are lvalues:
An expression which is a name, except for enumerators and member functions.
An expression of type "lvalue reference to something."
The result of dereferencing a pointer.
Notice that ref_myvar, ref_foo, and rref are all lvalues because they have a name. std::move(ref_myvar) doesn't have a name, and so it's an rvalue.
Somebody generalized the statement "Temporaries are rvalues". I said "no" and gave him the following example
double k=3;
double& foo()
{
return k;
}
int main()
{
foo()=3; //foo() creates a temporary which is an lvalue
}
Is my interpretation correct?
Temporaries and rvalues are different (but related) concepts. Being temporary is a property of an object. Examples of objects that aren't tempory are local objects, global objects and dynamically created objects.
Being an rvalue is a property of an expression. The opposite of rvalues are lvalues such as names or dereferenced pointers. The statement "Temporaries are rvalues" is meaningless. Here is the relationsip between rvalues and temporary objects:
An rvalue is an expression whose evaluation creates a temporary object which is destroyed at the end of the full-expression that lexically contains the rvalue.
Note that lvalues can also denote temporary objects!
void blah(const std::string& s);
blah(std::string("test"));
Inside the function blah, the lvalue s denotes the temporary object created by evaluating the expression std::string("test").
Your comment "references are lvalues" is also meaningless. A reference is not an expression and thus cannot be an lvalue. What you really mean is:
The expression function() is an lvalue if the function returns a reference.
No. You are returning a reference to an global double, not a temporary.
The same test with a real temporary would be:
double foo() { return 3.0; }
int main() {
foo() = 2.0; // error: lvalue required as left operand of assignment
}
EDIT:
The answer was meant just to identify that the example was wrong, and I did not really want to get into the deeper discussion of whether temporaries are or not rvalues... As others have said, lvalue-ness or rvalue-ness are properties of an expression and not of the object (in the most general sense, not only class instances). Then again, the standard says that:
§3.10/5 The result of calling a function that does not return a reference is an rvalue. User defined operators are functions, and whether such operators expect or yield lvalues is determined by their parameter and return types.
§3.10/6 An expression which holds a temporary object resulting from a cast to a nonreference type is an rvalue (this includes the explicit creation of an object using functional notation (5.2.3)).
Which AFAIK are the circumstances under which temporaries are created. Now, it is also true that you can bind a constant reference to a temporary, in which case you will get a new variable (the reference) that can be used as an lvalue that effectively refers to the temporary object.
The fine line is that expressions that create temporaries are rvalue expressions. You can bind a constant reference to the result of that expression to obtain a variable that can be used as an const-qualified lvalue expression.
Temporaries were so consistently protected from becoming lvalues, that they are now called rvalues. But C++0x will allow temporaries to become lvalues thanks to move semantics. Like in this dumb snippet
void blah(ICanBeTemporary && temp)
{
temp.data = 2; //here temporary becomes lvalue
}
//somewhere
blah(ICanBeTemporary("yes I can"));
Now we have terminology mess. People used to call temporaries rvalues and this is called rvalue reference. Named objects are now considered to be non-rvalue referenced.