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.
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.
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
Basically, compiler magic. The Standard describes the rules, the compiler maker just has to figure out how to implement the rules.
In practice, references are either optimized out or implemented as pointer on CPU level.
std::move
isn't really special in that sense. It has a lvalue reference as input, and an rvalue reference as output. The compiler just has to apply the rvalue reference rules to the input.
Similarly, the goal of std::forward<T>
is just to tell the compiler to apply a different set of rules to the argument, rules which happen to be defined so that perfect forwarding works. The function itself does nothing.
来源:https://stackoverflow.com/questions/28458616/how-is-it-possible-to-get-a-reference-to-an-rvalue