2015-02-25 3 views
2

У меня есть объект, операция копирования которого была бы слишком медленной, поэтому я решил сделать это delete и заставить пользователей двигаться только. Копия этого объекта не имеет никакого смысла. Но тогда у меня есть эта функция:Возвращаемый объект, который не может быть скопирован значением

Object loadFromFile(const std::string& name) { 
    Object obj; 
    ... 
    return obj; 
} 

Даже если копия элизия происходит здесь и не конструктор копирования не вызывается, это не может скомпилировать, потому что конструктор копирования требуется существовать и быть доступным. Это моя вторая попытка:

Object&& loadFromFile(const std::string& name) { 
    Object obj; 
    ... 
    return std::move(obj); 
} 

Это компилируется. Ура!

Но новая проблема скачков при попытке использовать его:

Object x = loadFromFile("test.txt"); 

Это снова требует конструктор копирования. Я не мог заставить его работать даже явно используя ход:

Object x = std::move(loadFromFile("test.txt")); 

Единственным решением, которое я пришел было:

const Object& x = loadFromFile("test.txt"); 

Но x должен быть неконстантным, как он собирается быть изменен позже ,

Как с этим бороться?

+1

«Копия этого объекта не имеет никакого смысла в любом случае». судя по тому, как вы его используете, это выглядит неправдой. –

+2

Wait - не будет ли первый пример обрабатывать 'obj' как rvalue, и поэтому конструктор копирования не требуется * not *? [См. Здесь] (http://ideone.com/g4rAnN). Похоже, ваш вопрос основан на проблеме, которая не существует. –

+0

Правильно работает здесь: https://ideone.com/Ex81gj – Jarod42

ответ

3

Это один неправильно:

Object&& loadFromFile(const std::string& name) { 
    Object obj; 
    ... 
    return std::move(obj); 
} 

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

Второй прав:

Object loadFromFile(const std::string& name) { 
    Object obj; 
    ... 
    return obj; 
} 

Действительно, в этом случае поиск сначала выполняется так, как будто obj был Rvalue (Standard 12.8.32):

Когда критерии выполняется исключение операции копирования/перемещения, но не для объявления исключения, а подлежащий копированию объект обозначается lvalue или , когда выражение в операторе return является (возможно, в скобках) id-выражением, которое именует объект с автоматической продолжительностью хранения decla красный в теле или параметр-объявление-предложение самой внутренней закрывающей функции или лямбда-выражения, разрешение перегрузки для выбора конструктора для копии сначала выполняется так, как если бы объект был обозначен rvalue.Если первое разрешение перегрузки завершилось с ошибкой или не было выполнено, или если тип первого параметра выбранного конструктора не является ссылкой rvalue на тип объекта (возможно, с квалификацией cv), разрешение перегрузки выполняется снова, рассматривая объект как именующий. [Примечание. Это двухступенчатое разрешение перегрузки должно выполняться независимо от того, произойдет ли копирование. Он определяет вызывающий конструктор, если исключение не выполняется, и выбранный конструктор должен быть доступен, даже если вызов отменен. -end note]

И необходимо выбрать конструктор перемещения Object(Object&&).

+3

Ваш второй фрагмент работает с удаленным конструктором копии. –

+0

И он не только «работает» по совпадению, но и должен работать, поскольку «obj» - это энергонезависимый автоматический объект того же типа с квалификацией, что и тип возврата. Он выполняет все критерии, требующие от компилятора конструировать его на месте объекта, который принимает возвращаемое значение функции, и компилятору даже разрешено делать это, когда присутствует конструктор перемещения (или копирования), который имеет наблюдаемые побочные эффекты. – Damon

+2

'std :: move (obj)' является пессимизацией (он блокирует RVO), а вторая версия будет двигаться в любом случае, потому что в этом контексте компилятор должен сначала выполнить обработку перегрузки, обрабатывая «obj» как rvalue. @Damon copy elision никогда не является обязательным. –

0

Можете ли вы передать его как выходной параметр? Что-то вроде:

void loadFromFile(const std::string& name, Object& obj) { 
    //Operations on obj 
} 
2

Ops, моя ошибка, извините.

Я удалил конструктор копирования, но на самом деле не реализовал ход, предполагая, что он уже будет там. Проблема с созданием конструктора перемещения.

Спасибо за свет @ Джосеф Мэнсфилд.