Допустим, у меня есть (тривиальный) класс, который может быть перемещаемым и назначаемым, но не конструируемым и не назначаемым для копирования:
class movable
{
public:
explicit movable(int) {}
movable(movable&&) {}
movable& operator=(movable&&) { return *this; }
movable(const movable&) = delete;
movable& operator=(const movable&) = delete;
};
Это отлично работает:
movable m1(movable(17));
Это, конечно, не работает, потому что m1
не является значением
movable m2(m1);
Но я могу завернуть m1
в std::move
, который приводит его к rvalue-ссылке, чтобы он работал:
movable m2(std::move(m1));
Все идет нормально. Теперь, скажем, у меня есть (одинаково тривиальный) контейнерный класс, который содержит одно значение:
template <typename T>
class container
{
public:
explicit container(T&& value) : value_(value) {}
private:
T value_;
};
Это, однако, не работает:
container<movable> c(movable(17));
Компилятор (я пробовал clang 4.0 и g ++ 4.7.2) жалуется, что я пытаюсь использовать movable
удаленный экземпляр-конструктор в container
список инициализации. Опять оборачиваем value
в std::move
заставляет это работать:
explicit container(T&& value) : value_(std::move(value)) {}
Но почему std::move
нужен в этом случае? не value
уже типа movable&&
? Как value_(value)
отличный от movable m1(movable(42))
?
Это потому что value
является именованной переменной и, следовательно, lvalue. std::move
требуется привести его обратно к r-значению, так что это вызовет перегрузку move-constructor T
чтобы соответствовать.
Чтобы сказать это по-другому: ссылка Rvalue может привязывать к значению, но оно само по себе не является значением. Это просто ссылка, а в выражении это lvalue. Единственный способ создать из него выражение, представляющее собой rvalue, — это приведение.
Как
value_(value)
отличный отmovable m1(movable(42))
?
Именованная ссылка на rvalue является lvalue (и, таким образом, будет привязана к ctor удаленной копии), тогда как временная является, ну, в общем, rvalue (prvalue, чтобы быть конкретным).
§5 [expr] p6
[…] В общем, эффект этого правила заключается в том, что именованные ссылки на rvalue обрабатываются как lvalues, а безымянные ссылки на rvalue на объекты обрабатываются как xvalue […]
Как и из примера:
A&& ar = static_cast<A&&>(a);
Выражение
ar
это значение.
Приведенные выше цитаты взяты из ненормативных примечаний, но являются адекватным объяснением, так как остальная часть пункта 5 идет и объясняет, какие выражения только создать xvalues† (иначе, только указанные выражения и никто другой не создаст значения x). Смотрите также Вот для исчерпывающего списка.
† Значения xvalues представляют собой одну подгруппу значений r, а prvalues - другую подгруппу. Увидеть этот вопрос для объяснения.