I need some reassurance about whenever, assigning or list initializing an auto
typed named variable, with
std::move
()ed returned reference to a variablewhere after the expression, the origin gets out of scope, is A safe / B unsafe. Example code:
#include <iostream>
#include <string>
#include <deque>
#include <utility>
int main() {
std::deque<std::string> container {"foo"};
auto elementB = container.front(); //B I assume this is unsafe
auto elementA = std::move(container.front());//A I assume this is safe
container.pop_front();
std::cout << "A: " << elementA << " B: " << elementB << "\n";
}
As far as I understand expression B generates a lvalue right of the assignment and so the type of elementB
is a lvalue reference aka std::string&
and so that one would be unsafe.
Also the output of "A: foo B: " when executing the code suggests that. ( https://ideone.com/wKKbdK ) Update: Sorry I forgot that I moved it, so I changed the order and now the output is as excepted, sorry.
However the much more troublesome thing where I am unsure is expression A: After the std::move
I assume I got an xvalue which is both a rvalue and a glvalue, so I am not sure whats the standardized behavior if any, for the type deduction of elementA
.
Because from lvalues I almost sure its UB, and lvalues are glvalues, and xvalues are part of them, then the type would of elementA
would be std::string&&
, which would be unsafe right? (unless the exception for const&& AFAIK)
So to summarize: Is the usage of elementA safe standardized behaviour and what will be its type?
Is the usage of elementA safe standardized behaviour?
Yes.
... and what will be its type?
It's type will be std::string
. auto
type deduction works like template type deduction, and that includes removing the "referenceness" of a reference. The fact that std::move(container.front())
returns an xvalue doesn't really change much here. It is an "expiring" value, you can either (a) move-construct a new object (as you currently do) (b) bind it to a const
-qualified reference or (c) bind it to an rvalue-reference. Here, (b) and (c) both work but don't make much sense, as they obscure the fact that nothing is moved-from (thanks to @M.M for correcting me here). Example:
auto elementA = std::move(container.front());
// Error, can't bind to non-const reference:
// auto& doesntWork = std::move(container.front());
auto&& thisWorks = std::move(container.front());
const auto& thisWorksToo = std::move(container.front());
Note that as @M.M pointed out in the comments, the last two references will be dangling once container.pop_front();
is encountered.
Also note that the deduction of elementB
as std::string
doesn't help the fact that you are dereferencing a moved-from object (return by container.front()
), which you should avoid.