Я перепутан с unique_ptr и философией перемещения rvalue.
Скажем, у нас есть два набора:
std::vector<std::auto_ptr<int>> autoCollection;
std::vector<std::unique_ptr<int>> uniqueCollection;
Теперь я ожидал бы, что следующее перестанет работать, поскольку нет никакого сообщения, что алгоритм делает внутренне и возможно делает внутренние копии центра и т.п., таким образом разрывая далеко владение от auto_ptr:
std::sort(autoCollection.begin(), autoCollection.end());
Я получаю это. И компилятор справедливо запрещает этот случай.
Но затем я делаю это:
std::sort(uniqueCollection.begin(), uniqueCollection.end());
И это компилирует. И я не понимаю почему. Я не думал, что unique_ptrs мог быть скопирован. Это означает, что значение центра не может быть принято, таким образом, вид менее эффективен? Или этот центр является на самом деле перемещением, которое на самом деле так же опасно как набор auto_ptrs и должно быть запрещено компилятором?
Я думаю, что пропускаю некоторую решающую информацию, таким образом, я нетерпеливо жду кого-то для предоставления меня ага! момент.
Я думаю, что это больше вопрос философии, чем техники :)
Основной вопрос в том, в чем разница между Move и Copy. Я не буду переходить на технический/стандартизированный язык, давайте сделаем это просто:
Как вы сказали, можно реализовать Move в терминах Copy: создать копию в новом месте и отбросить оригинал. Однако здесь есть две проблемы. Первая - производительность, вторая - объекты, используемые для RAII: кто из двух должен иметь право собственности?
Правильный конструктор Move решает эти две проблемы:
Очень хорошей иллюстрацией этого являются auto_ptr
и unique_ptr
.
С auto_ptr
у вас испорчена семантика Copy: оригинал и копия не равны. Вы можете использовать его для семантики Move, но есть риск, что вы потеряете объект, на который указываете.
С другой стороны, unique_ptr
именно таков: он гарантирует уникальность владельца ресурса, что позволяет избежать копирования и неизбежной проблемы удаления, которая за этим последует. Причем отсутствие копирования гарантируется и во время компиляции. Поэтому он подходит для контейнеров до тех пор, пока вы не пытаетесь инициализировать копию.
typedef std::unique_ptr<int> unique_t;
typedef std::vector< unique_t > vector_t;
vector_t vec1; // fine
vector_t vec2(5, unique_t(new Foo)); // Error (Copy)
vector_t vec3(vec1.begin(), vec1.end()); // Error (Copy)
vector_t vec3(make_move_iterator(vec1.begin()), make_move_iterator(vec1.end()));
// Courtesy of sehe
std::sort(vec1.begin(), vec1.end()); // fine, because using Move Assignment Operator
std::copy(vec1.begin(), vec1.end(), std::back_inserter(vec2)); // Error (copy)
Таким образом, вы можете использовать unique_ptr
в контейнере (в отличие от auto_ptr
), но ряд операций будет невозможен, поскольку они включают копирование, которое тип не поддерживает.
К сожалению, Visual Studio может быть довольно вялой в применении стандарта, а также имеет ряд расширений, которые необходимо отключить для обеспечения переносимости кода... не используйте ее для проверки стандарта :)
.Перемещение unique_ptr
ов осуществляется с помощью их конструктора перемещения. unique_ptr
является Movable, но не CopyConstructable.
Есть отличная статья о ссылках rvalue здесь. Если вы еще не читали о них или запутались, посмотрите!