С++ 11 move (x) на самом деле означает static_cast <X &&> (x)?

Просто прочитав язык программирования Stroustrup С++ 4th Ed и в главе 7 он говорит:

move(x) означает static_cast<X&&>(x), где X - тип x

и

Так как move (x) не перемещает x (он просто создает ссылку rvalue на x) было бы лучше, если move() был назван rval()

Мой вопрос: если move() просто превращает переменную в rval, каков фактический механизм, который достигает "перемещения" ссылки на переменную (путем обновления указателя)?

Я думал, что move() - это как конструктор перемещения, за исключением того, что клиент может использовать move() для принудительного компилятора

Ответ 1

каков фактический механизм, который достигает "перемещения" ссылки на переменную (путем обновления указателя)?

Передача его функции (или конструктору), которая принимает ссылку rvalue, и перемещает значение из этой ссылки. Без трансляции переменные не могут связываться с rvalue-ссылками и поэтому не могут быть переданы такой функции - это предотвращает случайное перемещение переменных.

Я думал, что move() - это как конструктор перемещения, за исключением того, что клиент может использовать move() для принудительного компилятора

Нет; он использовал для преобразования lvalue в rvalue, чтобы передать его конструктору перемещения (или другой движущейся функции), который требует ссылки rvalue.

typedef std::unique_ptr<int> noncopyable;  // Example of a noncopyable type
noncopyable x;
noncopyable y(x); // Error: no copy constructor, and can't implicitly move from x
noncopyable z(std::move(x)); // OK: convert to rvalue, then use move constructor

Ответ 2

Когда вы вызываете move, вы просто говорите "Эй, я хочу переместить этот объект". И когда конструктор принимает rvalue-reference, он понимает это как "Хм, кто-то хочет, чтобы я переместил данные из этого объекта в себя. Итак, хорошо, я сделаю это".

std::move не перемещает и не изменяет объект, он просто "маркирует" его как "готовый к движению". И только функция, которая принимает ссылку rvalue, должна реализовать движущийся фактический объект.

Это пример, который описывает текст выше:

#include <iostream>
#include <utility>

class Foo
{
public:
    Foo(std::size_t n): _array(new int[n])
    {

    }

    Foo(Foo&& foo): _array(foo._array)
    {
        // Hmm, someone tells, that this object is no longer needed
        // I will move it into myself
        foo._array = nullptr;
    }

    ~Foo()
    {
        delete[] _array;
    }

private:
    int* _array;
};

int main()
{
    Foo f1(5);

    // Hey, constructor, I want you move this object, please
    Foo f2(std::move(f1));

    return 0;
}

Ответ 3

Как и в Going Native 2013, Скотт Мейерс рассказал о возможностях С++ 11, включая move.

То, что std::move по существу, означает "безоговорочно отбрасывается на rvalue".

Мой вопрос: если move() просто превращает переменную в rval, каков фактический механизм, который достигает "перемещения" ссылки на переменную (путем обновления указателя)?

move выполняет листинг типов, поэтому компилятор будет знать, какой ctor использовать. Фактическая операция move выполняется движением ctor. Вы можете использовать его как функцию перегрузки. (ctor перегружает с параметром параметра rvalue.)

Ответ 4

из http://en.cppreference.com/w/cpp/utility/move

std:: move получает ссылку rvalue на свой аргумент и преобразует ее к значению x.

Код, который получает такое значение x, имеет возможность оптимизировать ненужные накладные расходы, перемещая данные из аргумента, оставляя его в действительном, но неуказанном состоянии.

Возвращаемое значение

static_cast<typename std::remove_reference<T>::type&&>(t)

вы можете видеть, что перемещение - это просто static_cast

путем вызова std::move объекта не делает ничего полезного, однако он сообщает, что возвращаемое значение может быть изменено на "действительное, но неуказанное состояние"

Ответ 5

Я думал, что move() - это как конструктор перемещения, за исключением того, что клиент может используйте move(), чтобы заставить компилятор

По существу, приведение типа к типу r-значения, это позволяет компилятору вызывать конструктор перемещения над конструктором копирования.

Ответ 6

std::move эквивалентен static_cast<std::string&&>(x).

В стандарте он определяется следующим образом:

template <class T>
constexpr remove_reference_t<T>&& move(T&&) noexcept;

Ответ 7

как упомянутые в его книге страустры, движение - это неправильное имя, но rval будет лучше. механизм просто принимает указатель из одного экземпляра (скажем, inst_1) в другой (inst_2) и нулевой первый.

inst_1 = std::move(inst_2);

это движение просто сказано только для того, чтобы отличаться от типа вида. это не значение, это не ссылка, а ссылка r-value.

механизм делает то, что вам или программисту, который сделал класс, хочет. рассмотреть следующие вопросы:

class foo {
  int*   bar_;
  size_t size_;
public:
  void do_it(foo& cpy) {   // copy
    // copy the size
    bar_ = new int[size_ = cpy.size_];

    // copy the array
    for(auto i=0; i<size_t; i++)
      bar_[i] = cpy.bar_[i];
  }

  void do_it(foo&& mov) {  // move
    // copy the pointer
    bar_ = mov.bar_;
    // copy the size
    size_ = mov.size_;

    // null the pointer and size
    mov.bar_ = nullptr;
    mov.size_ = 0;

    // the pointer and size is now moved or stolen...
  }
};

первая - это копия l-value do_it, а вторая - одно значение r. а не std:: move перемещает члены instace, но функция, определяемая значением l/r. movee превращает , если возможно экземпляр в значение r.

foo inst_1, inst_2;
inst_1.do_it(inst_2);  // copy
inst_1.do_it(std::move(inst_2));  // move

также то, почему, вероятно, было бы лучше назвать его rval: inst_1.do_it(std::rval(inst_2)); имел бы больше смысла в таком поведении.

Ответ 8

rvalues ​​обычно являются временными значениями, которые отбрасываются и уничтожаются сразу после создания (за некоторыми исключениями). std::string&& - это ссылка на std::string, которая будет привязана только к rvalue. До С++ 11 временные файлы привязывались бы только к std::string const& - после С++ 11, они также привязывались к std::string&&.

Переменная типа std::string&& ведет себя как стандартная ссылка на болото. Это в значительной степени связано только с привязкой сигнатур функций, которые std::string&& отличается от переменных std::string&.

С другой стороны, если функция возвращает std::string&&, она сильно отличается от возврата std::string&, потому что второй тип вещи, который может быть привязан к std::string&&, является возвращаемым значением функции возврат std::string&&.

std::move - наиболее распространенный способ генерации такой функции. В некотором смысле, он лежит в контексте, в котором он находится, и говорит ему: "Я временно, делай со мной, что хочешь". Итак, std::move ссылается на что-то, и делает бросок, который заставляет его притворяться временным - aka, rvalue.

Перемещение конструкторов и перемещение назначений, а другие функции управления перемещением принимают ссылку rvalue, чтобы знать, когда данные, которые они передают, являются "царапанными" данными, которые они могут "повредить" в некоторой степени при ее использовании. Это очень полезно, потому что многие типы (от контейнеров, до std::function, до всего, что использует шаблон pImpl, для не копируемых ресурсов) могут иметь внутреннее состояние намного легче, чем его можно скопировать. Такой ход изменяет состояние исходного объекта, но поскольку функции говорят, что это данные с нуля, это не невежливо.

Итак, move происходит не в std::move, а в функции, которая понимает, что возвращаемое значение std::move подразумевает, что разрешено несколько раз изменять данные, если это поможет.

Другие способы получения значения rvalue или указание на то, что исходный объект является "данными с нуля", - это когда у вас есть истинный временный (анонимный объект, созданный как возврат какой-либо другой функции или созданный с помощью функции -строчный синтаксис конструктора) или при возврате из функции с выражением формы return local_variable;. В обоих случаях данные привязываются к rvalue-ссылкам.

Короткий вариант состоит в том, что std::move не перемещается, а std::forward не пересылает, он просто указывает, что такое действие будет разрешено в этой точке и позволяет вызываемой функции/конструктору решать, что делать с эту информацию.

Ответ 9

В дополнение к другим ответам, пример может помочь вам лучше понять, как работают ссылки на rvalue. Взгляните на следующий код, который эмулирует ссылки rvalue:

#include <iostream> 
#include <memory>

template <class T>
 struct rvalue_ref
 {
    rvalue_ref(T& obj) : obj_ptr{std::addressof(obj)} {} 

    T* operator->() //For simplicity, we'll use the reference as a pointer.
    { return obj_ptr; }

    T* obj_ptr; 
 };

template <class T>
 rvalue_ref<T> move(T& obj)
 {
    return rvalue_ref<T>(obj);
 }


template <class T>
 struct myvector
 {
    myvector(unsigned sz) : data{new T[sz]} {}

    myvector(rvalue_ref<myvector> other) //Move constructor
    {
        this->data = other->data;
        other->data = nullptr;
    }

    ~myvector()
    {
       delete[] data;
    }

    T* data; 
 }; 

int main()
{
    myvector<int> vec(5); //vector of five integers

    std::cout << vec.data << '\n'; //Print address of data

    myvector<int> vec2 = move(vec); //Move data from vec to vec2

    std::cout << vec.data << '\n'; //Prints zero

    //Prints address of moved data (same as first output line)
    std::cout << vec2.data << '\n'; 
}

Как мы видим, "move" генерирует только правильный псевдоним, чтобы указать компилятору, который требуется использовать для перегрузки конструктора. Разница между этой реализацией и действительными ссылками rvalue, конечно, заключается в том, что приведение к rvalue reference имеет нулевые служебные данные, поскольку это только директива компилятора.