Почему мне нужно использовать std :: move в списке инициализации move-constructor?

Допустим, у меня есть (тривиальный) класс, который может быть перемещаемым и назначаемым, но не конструируемым и не назначаемым для копирования:

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))?

14

Решение

Это потому что value является именованной переменной и, следовательно, lvalue. std::move требуется привести его обратно к r-значению, так что это вызовет перегрузку move-constructor T чтобы соответствовать.

Чтобы сказать это по-другому: ссылка Rvalue может привязывать к значению, но оно само по себе не является значением. Это просто ссылка, а в выражении это lvalue. Единственный способ создать из него выражение, представляющее собой rvalue, — это приведение.

16

Другие решения

Как 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 ​​- другую подгруппу. Увидеть этот вопрос для объяснения.

5

По вопросам рекламы [email protected]