Возможный дубликат:
Может ли кто-нибудь объяснить мне семантику перемещения?
Может ли кто-нибудь указать мне хороший источник или объяснить его здесь, что такое семантика перемещения?
Возможный дубликат:
Может ли кто-нибудь объяснить мне семантику перемещения?
Может ли кто-нибудь указать мне хороший источник или объяснить его здесь, что такое семантика перемещения?
Забудьте о С++ 0x на данный момент. Семантика перемещения - это то, что не зависит от языка - С++ 0x просто предоставляет стандартный способ выполнения операций с семантикой перемещения.
Семантика перемещения определяет поведение определенных операций. Большую часть времени они контрастируют с семантикой копирования, поэтому было бы полезно сначала определить их.
Назначение с семантикой копирования имеет следующее поведение:
// Copy semantics
assert(b == c);
a = b;
assert(a == b && b == c);
то есть a
заканчивается равным b
, и мы оставляем b
без изменений.
Назначение с семантикой перемещения имеет более слабые почтовые условия:
// Move semantics
assert(b == c);
move(a, b); // not C++0x
assert(a == c);
Обратите внимание, что больше нет гарантии того, что b
останется неизменным после назначения с семантикой перемещения. Это принципиальная разница.
Одно из преимуществ семантики перемещения заключается в том, что она позволяет выполнять оптимизацию в определенных ситуациях. Рассмотрим следующий тип значения:
struct A { T* x; };
Предположим также, что мы определяем два объекта типа A
равными, если их член x
указывает на равные значения.
bool operator==(const A& lhs, const A& rhs) { return *lhs.x == *rhs.x; }
Наконец, предположим, что мы определили объект A
который будет единоличным владельцем пуантинга их элемента x
.
A::~A() { delete x; }
A::A(const A& rhs) : x(new T(rhs.x)) {}
A& A::operator=(const A& rhs) { if (this != &rhs) *x = *rhs.x; }
Теперь предположим, что мы хотим определить функцию для замены двух объектов A
Мы могли бы сделать это обычным способом с семантикой копирования.
void swap(A& a, A& b)
{
A t = a;
a = b;
b = t;
}
Однако это излишне неэффективно. Что мы делаем?
a
в t
.b
в a
.t
в b
.t
. Если объекты T
копировать дорого, то это расточительно. Если бы я попросил вас поменять два файла на вашем компьютере, вы бы не создали третий файл, а затем скопировали и вставили содержимое файла перед уничтожением вашего временного файла, не так ли? Нет, вы удалите один файл, переместите второй в первую позицию, а затем, наконец, переместите первый файл обратно во второй. Нет необходимости копировать данные.
В нашем случае легко перемещаться по объектам типа A
:
// Not C++0x
void move(A& lhs, A& rhs)
{
lhs.x = rhs.x;
rhs.x = nullptr;
}
Мы просто перемещаем указатель rhs
в lhs
а затем отказываемся от владения rhs
этим указателем (устанавливая его в null). Это должно пролить свет на то, почему более слабое состояние после семантики перемещения позволяет оптимизировать.
Определив эту новую операцию перемещения, мы можем определить оптимизированный своп:
void swap(A& a, A& b)
{
A t;
move(t, a);
move(a, b);
move(b, t);
}
Другое преимущество семантики перемещения заключается в том, что она позволяет перемещаться вокруг объектов, которые невозможно скопировать. Ярким примером этого является std::auto_ptr
.
С++ 0x позволяет перемещать семантику через функцию ссылки на rvalue. В частности, операции такого рода:
a = b;
Имейте семантику перемещения, когда b
является ссылкой на значение (пишется T&&
), в противном случае они имеют семантику копирования. Вы можете принудительны перемещение семантики с помощью std::move
функцию (отличную от move
я определен ранее), когда b
не является ссылка Rvalue:
a = std::move(b);
std::move
- простая функция, которая по существу преобразует свой аргумент в ссылку на rvalue. Обратите внимание, что результаты выражений (таких как вызов функции) автоматически являются rvalue ссылками, так что вы можете использовать семантику перемещения в этих случаях без изменения вашего кода.
Чтобы определить оптимизацию перемещения, вам нужно определить конструктор перемещения и оператор присваивания перемещения:
T::T(T&&);
T& operator=(T&&);
Так как эти операции имеют семантику перемещения, вы можете изменять передаваемые аргументы (при условии, что вы оставляете объект в разрушаемом состоянии).
Это, по сути, все, что нужно сделать. Обратите внимание, что ссылки на rvalue также используются для обеспечения идеальной пересылки в С++ 0x (из-за специально созданного взаимодействия системы типов между ссылками на rvalue и другими типами), но это на самом деле не связано с семантикой перемещения, поэтому я не обсуждал это здесь.
В принципе, ссылки rvalue позволяют вам определять, когда объекты являются временными, и вам не нужно сохранять их внутреннее состояние. Это позволяет использовать гораздо более эффективный код, который С++ 03 должен был копировать все время, в С++ 0x вы можете продолжать использовать одни и те же ресурсы. Кроме того, ссылки rvalue обеспечивают идеальную пересылку.
Посмотрите этот ответ.
Я прочитал тонну текстовых объяснений около года и не понял все о ссылках r-value , пока я не посмотрю эту отличную презентацию Скотта Мейера: http://skillsmatter.com/podcast/home/move-semanticsperfect-forwarding-and-rvalue-references
Он объясняет, что это смешно и достаточно медленно, чтобы понять каждую вещь, происходящую в процессах.
Я знаю, это 1h30, но на самом деле это лучшее объяснение, которое у меня было в прошлом году.
После того, как вы прочитали статьи (как и другие ответы), просмотр этого видео сопел в моем сознании последовательным образом и через несколько дней после того, как я смог объяснить это некоторым коллегам и объяснил, как использовать std:: unique_ptr (поскольку это связано - это только позволяет перемещать семантику, а не копировать), потому что она требует понимания std:: move(), которая требует понимания семантики перемещения.
рад видеть такой вопрос, и я рад поделиться своей мыслью. Я думаю, вы спрашиваете об исправлении ошибок в определении языка С++, а не только о другой функции языка С++. "Ошибка" существует уже десятки лет. То есть конструктор copy.
Конструкторы копирования кажутся очень странными, если вы знаете, что в физике есть много вещей, которые нельзя копировать как энергию и массу. Это просто шутка, но на самом деле в мире программирования тоже объекты, такие как эксклюзивные дескрипторы файлов, не копируются. Поэтому программисты и дизайнеры С++ придумали некоторые трюки, чтобы справиться с этим. Есть 3 известных: NRVO, boost::noncopyable
и std::auto_ptr
.
NRVO (Именованная оптимизация возвращаемого значения) - это техническая функция, которая позволяет функции возвращать объект по значению без вызова конструктора копирования. Но проблема с NRVO заключается в том, что хотя конструктор копирования фактически не вызван, требуется конструктор конструктора public
, что означает, что объекты boost::noncopyable
несовместимы с NRVO.
std::auto_ptr
- это еще одно испытание, чтобы обойти конструктор копирования. Возможно, вы видели его "конструктор копирования", реализованный как
template <typename _T>
auto_ptr(auto_ptr<_T>& source)
{
_ptr = source._ptr; // where _ptr is the pointer to the contained object
source._ptr = NULL;
}
Это вовсе не копия, а "move" . Вы можете рассматривать такое поведение как прототип семантического перемещения.
Но std::auto_ptr
также имеет свою проблему: он несовместим с контейнерами STL. Так что, к сожалению, все, что связано с неготовностью, является болезненным.
Это было болезненно, пока семантика перемещения С++ 0x окончательно не была опубликована и не реализована разработчиками компилятора.
Простым способом можно просто подумать о семантике перемещения как о том же, что и о "копировании" поведения std::auto_ptr
, но с полной поддержкой языковых функций, поэтому он отлично работает с контейнерами и алгоритмом.
Кстати, в С++ 0x std::auto_ptr
устарел и рекомендуется использовать новый тип шаблона std::unique_ptr
.
Моя история закончится. Пожалуйста, обратитесь к другим сообщениям, если вы хотите узнать больше об этом, как о странном синтаксисе и системе rvalue.