2016-09-30 3 views
13

Я пытаюсь вернуть члену std::unique_ptr class (попытка переместить право собственности) на вызывающего. Ниже приведен пример фрагмент код:Возвращаемый элемент unique_ptr из метода класса

class A { 
public: 
    A() : p {new int{10}} {} 

    static std::unique_ptr<int> Foo(A &a) { 
    return a.p; // ERROR: Copy constructor getting invoked 
       // return std::move(a.p); WORKS FINE 
    } 

    std::unique_ptr<int> p; 
}; 

Я думал, что компилятор (GCC-5.2.1) будет иметь возможность сделать оптимизацию возвращаемого значения (копия Пропуск) в этом случае, не требуя явное намерение через std::move(). Но это не так. Почему нет?

Следующий код, кажется, работает хорошо, что кажется эквивалент:

std::unique_ptr<int> foo() { 
    std::unique_ptr<int> p {new int{10}}; 
    return p; 
} 
+2

Это отличный первый вопрос. Добро пожаловать в StackOverflow! – Barry

ответ

11

Правило в [class.copy] является:

[...], когда выражение в return заявлении (возможно, в скобках) id-выражение, которое называет объект с автоматическим временем хранения, объявленным в теле, или parameter-decla ration-clause самой внутренней закрывающей функции или lambda-expression, разрешение перегрузки до . Выбор конструктора для копии сначала выполняется так, как если бы объект был обозначен rvalue.

В этом примере:

std::unique_ptr<int> foo() { 
    std::unique_ptr<int> p {new int{10}}; 
    return p; 
} 

p это имя объекта с автоматической продолжительности хранения, заявленной в теле функции. Поэтому вместо того, чтобы копировать его в возвращаемое значение, мы сначала попытаемся его переместить. Это прекрасно работает.

Но в этом примере:

static std::unique_ptr<int> Foo(A &a) { 
    return a.p; 
} 

, что не применяется. a.p - это не имя объекта вообще, поэтому мы не пытаемся перегрузить разрешение, как если бы оно было rvalue, вместо этого мы просто делаем обычную вещь: попробуйте скопировать его. Это не удается, поэтому вам нужно явно указать move().


Это формулировка правила, но это может не ответить на ваш вопрос. Почему это правило? В принципе - мы пытаемся быть в безопасности. Если мы назовем локальную переменную, всегда можно перейти от нее в оператор return. Он никогда больше не будет доступен. Легкая оптимизация, отсутствие недостатков. Но в вашем исходном примере a не принадлежит этой функции, и не является a.p. Из него небезопасно перемещаться, поэтому язык не будет пытаться делать это автоматически.

-1

Копирование сообщения не может применяться (помимо прочего), так как a.p является std::unique_ptr, что невозможно. И так как a.p имеет продолжительность жизни за пределами тела A::Foo(A&), было бы очень удивительно (как, например, удивить человека, написавшего код), если компилятор автоматически попытался переместиться с a.p, что, вероятно, разрушит инварианты класса a. Он будет работать, если вы return std::move(a.p);, но это явно крадет a.p.

+0

Копирование elision может применяться к типам только для перемещения (например, пример OP) – Barry

+0

@Barry OK, мое первое предложение неверно. Копирование elision также относится к ходам. Тем не менее, я считаю, что остальная часть ответа по-прежнему стоит. –

+0

@AndreKostur: Поскольку кодер явно пытается возвратить уникальный_ptr, чей конструктор копии удален, единственный вариант - это переход, и копия elision должна работать нормально (IMO). Меня действительно удивила ошибка компиляции, а не предупреждение. – axg

Смежные вопросы