Компилятор не терпит неудачу при возврате std :: unique_ptr в std :: vector - PullRequest
22 голосов
/ 07 июля 2019

unique_ptr нельзя сдвинуть обратно в std::vector, поскольку он не подлежит копированию, если не используется std::move.Однако, пусть F будет функцией, которая возвращает unique_ptr, тогда операция std::vector::push_back(F()) разрешена.Ниже приведен пример:

#include <iostream>
#include <vector>
#include <memory>

class A {
  public:
    int f() { return _f + 10; }

  private:
    int _f = 20;
};

std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); }


int main() {
  std::unique_ptr<A> p1(new A());

  std::vector< std::unique_ptr<A> > v;

  v.push_back(p1); // (1) This fails, should use std::move

  v.push_back(create()); // (2) This doesn't fail, should use std::move?

  return 0;
}

(2) разрешено, а (1) - нет.Это потому, что возвращаемое значение перемещается каким-то образом неявно?

В (2), действительно ли необходимо использовать std::move?

Ответы [ 4 ]

34 голосов
/ 07 июля 2019

std::move(X), по сути, означает «здесь, относитесь к X как к временному объекту».

create() для начала возвращает временное значение std::unique_ptr<A>, поэтому move не нужно.


Если вы хотите узнать больше, изучите категории значений .Ваш компилятор использует категории значений, чтобы определить, ссылается ли выражение на временный объект («rvalue») или нет («lvalue»).

p1 - это lvalue, а create() - это rvalue.

8 голосов
/ 08 июля 2019

std::vector::push_back() имеет перегрузку, которая принимает значение rvalue в качестве ввода:

void push_back( T&& value );

Возвращаемое значение create() является неназванным временным, то есть rvalue, поэтому его можно передать как есть на push_back() без необходимости использовать std::move() для него.

std::move() требуется только при передаче именованной переменной, то есть lvalue, где ожидается rvalue.

5 голосов
/ 07 июля 2019

С C ++ 11 мы получили конструкторы перемещения и семантику rvalues.

std :: move (X) - это просто приведение к r-значению, которое преобразует X в X &&, то есть в него.Чем Move ctor берет на себя работу и перемещает конструкторы, как правило, «крадет» ресурсы, удерживаемые аргументом.unique_ptr есть ход ctor.

Возвращаемые значения функции уже являются rvalue (если только функция не возвращает ссылку lvalue, как указано @HolyBlackCat в комментариях), которая запускает ctor перемещения без необходимости дополнительного приведения.И так как Move Ctor определен для unique_ptr, он скомпилируется.

Также причина сбоя v.push_back (p1); заключается в следующем: вы пытаетесь вызвать конструктор копирования с lvalue, и это не удается, потому что unique_ptr не имеет копии ctor.

2 голосов
/ 08 июля 2019

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

#include <iostream>
#include <vector>
#include <memory>

class A {
  public:
    int f() { return _f + 10; }

  private:
    int _f = 20;
};

std::unique_ptr<A> create() {
    std::unique_ptr<A> x (new A);
    return x; 

}


int main() {
  std::unique_ptr<A> p1(new A());

  std::vector< std::unique_ptr<A> > v;

  //v.push_back(p1); // (1) This fails, should use std::move

  v.push_back(create()); // (2) This doesn't fail, should use std::move?

  return 0;
}
...