I have used std::move
and std::forward
in C++. My question is: how are these functions actually implemented by the standard library?
If an lvalue is something you can get the address of, and an rvalue is exclusively not an lvalue, how can you actually implement these references?
Do these new facilities allow for something like:
auto x = &(3);
or something like that? Can you get a reference to an rvalue that isn't just a std::move
/forward
returned lvalue?
Hopefully these questions make sense. I couldn't find good information on Google, just tutorials on perfect forwarding, etc.
How is it possible to get a reference to an rvalue?
Conceptually, an rvalue expression creates a temporary object, or sometimes denotes an existing object. That can be bound to a reference like any other object; but, to avoid confusion, the language only allows that for rvalue and const
lvalue references.
I have used std::move and std::forward in C++. My issue is how this is actually implemented by the compiler?
move
simply returns an rvalue reference to its argument, equivalent to
static_cast<typename remove_reference<T>::type&&>(t)
The result of the function call is an rvalue (specifically, an xvalue), so it can be bound to an rvalue reference where the function argument couldn't. This allows you to explicitly move from an lvalue, using move
to convert it to an rvalue, while not allowing you to accidentally move from it.
forward
is similar, but overloaded to return an rvalue reference to an rvalue or rvalue reference, and an lvalue reference to anything else.
If an l-value is something you can get the address of
That's more or less correct. The official definition is that the expression "designates a function or an object", and those are things that have addresses.
and an r-value is exclusively not an l-value
Not really. Simplifying slightly, an expression is either a lvalue or an rvalue, but can be converted from one to the other. An lvalue can be implicitly converted to an rvalue; converting the other way can be done with a cast, as move
does.
how can you actually implement these references?
Just like any other reference - as an alias for, or a pointer to, the object it's bound to. The only difference is which kinds of expression can be used to denote (and possibly create) the object that's bound to the reference.
Do these new facilities allow for something like
auto x = &(3);
That attempts to take the address of an rvalue directly, which isn't allowed. Since the question is about references, not pointers, the following are allowed, binding a reference to a temporary object (whose lifetime is extended to match the reference):
auto && rvalue = 3;
auto const & const_lvalue = 3;
while it's not allowed to bind it to a non-const lvalue reference
auto & lvalue = 3; // ERROR
I cannot call a function: void foo(string* bar)
like this: foo(&string("Hello World!"))
or I get an error:
error: taking address of temporary
I also cannot call a function: void foo(string& bar)
like this: foo(string("Hello World!"))
or I get an error:
error: invalid initialization of non-const reference of type 'std::string& {aka std::basic_string&}' from an rvalue of type 'std::string {aka std::basic_string}'
What C++11 has provided me the ability to do is to make an rvalue reference, so I can call a function: void foo(string&& bar)
like this: foo(string("Hello World!"));
Furthermore, internally to foo
I can get the address of the object passed in by an rvalue reference:
void foo(string&& bar){
string* temp = &bar;
cout << *temp << " @:" << temp << endl;
}
It seems like the OP has a really good grip on rvalues. But this explanation of them was helpful to me, and may be to others. It goes into a bit of detail about why C++03 allowed constant references to rvalues, versus C++11's rvalue references.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With