Как применить семантику перемещения при росте вектора? - PullRequest
78 голосов
/ 04 ноября 2011

У меня есть std::vector объектов определенного класса A.Класс нетривиален и имеет конструкторы копирования и определенные конструкторы перемещения.

std::vector<A>  myvec;

Если я заполню вектор A объектами (используя, например, myvec.push_back(a)), вектор увеличится в размере, используя конструктор копирования A( const A&) для создания новых копий элементовв векторе.

Могу ли я каким-то образом принудительно использовать вместо этого конструктор перемещения класса A?

Ответы [ 3 ]

109 голосов
/ 14 января 2012

Вы должны сообщить C ++ (в частности, std::vector), что ваш конструктор перемещения и деструктор не выдает, используя noexcept. Тогда конструктор перемещения будет вызываться при увеличении вектора.

Это как объявить и реализовать конструктор перемещения, который соблюдается std::vector:

A(A && rhs) noexcept { 
  std::cout << "i am the move constr" <<std::endl;
  ... some code doing the move ...  
  m_value=std::move(rhs.m_value) ; // etc...
}

Если конструктор не noexcept, std::vector не может его использовать, так как он не может гарантировать гарантии исключений, требуемые стандартом.

Подробнее о том, что сказано в стандарте, читайте C ++ Переместить семантику и исключения

Благодарю Бо, который намекнул, что это может быть связано с исключениями. Также примите во внимание совет Kerrek SB и используйте emplace_back, когда это возможно. Это может быть быстрее (но часто это не так), оно может быть яснее и компактнее, но есть и некоторые подводные камни (особенно с неявными конструкторами).

Редактировать , часто по умолчанию то, что вы хотите: переместить все, что можно переместить, скопировать остальные. Чтобы явно попросить об этом, напишите

A(A && rhs) = default;

После этого вы получите noexcept, когда это возможно: Определен ли конструктор Move по умолчанию как noexcept?

Обратите внимание, что более ранние версии Visual Studio 2015 и более ранние версии не поддерживали это, хотя и поддерживают семантику перемещения.

16 голосов
/ 14 марта 2013

Интересно, что вектор gcc 4.7.2 использует конструктор перемещения только в том случае, если и конструктор перемещения, и деструктор имеют значение noexcept. Простой пример:

struct foo {
    foo() {}
    foo( const foo & ) noexcept { std::cout << "copy\n"; }
    foo( foo && ) noexcept { std::cout << "move\n"; }
    ~foo() noexcept {}
};

int main() {
    std::vector< foo > v;
    for ( int i = 0; i < 3; ++i ) v.emplace_back();
}

Выводит ожидаемое:

move
move
move

Однако, когда я удаляю noexcept из ~foo(), результат будет другим:

copy
copy
copy

Я думаю, это также отвечает на этот вопрос .

0 голосов
/ 30 октября 2017

Похоже, что единственный способ (для C ++ 17 и более ранних версий) std::vector использовать семантику перемещения при перераспределении - это удалить конструктор копии :). Таким образом, во время компиляции он будет использовать ваши конструкторы перемещения или пытаться умереть:).

Существует много правил, в которых std::vector НЕ ДОЛЖЕН использовать конструктор перемещения при перераспределении, но ничего не говорится о том, где он ДОЛЖЕН ИСПОЛЬЗОВАТЬ it.

template<class T>
class move_only : public T{
public:
   move_only(){}
   move_only(const move_only&) = delete;
   move_only(move_only&&) noexcept {};
   ~move_only() noexcept {};

   using T::T;   
};

Живите

или

template<class T>
struct move_only{
   T value;

   template<class Arg, class ...Args, typename = std::enable_if_t<
            !std::is_same_v<move_only<T>&&, Arg >
            && !std::is_same_v<const move_only<T>&, Arg >
    >>
   move_only(Arg&& arg, Args&&... args)
      :value(std::forward<Arg>(arg), std::forward<Args>(args)...)
   {}

   move_only(){}
   move_only(const move_only&) = delete;   
   move_only(move_only&& other) noexcept : value(std::move(other.value)) {};    
   ~move_only() noexcept {};   
};

Живой код

Ваш класс T должен иметь noexcept конструктор перемещения / оператор присвоения и noexcept деструктор. В противном случае вы получите ошибку компиляции.

std::vector<move_only<MyClass>> vec;
...