У меня есть std::vector
объекты определенного класса A
. Класс нетривиален и имеет конструкторы копирования и перемещение конструктор определены.
std::vector<A> myvec;
Если я заполню вектор A
объектами (например, используя myvec.push_back(a)
), вектор будет увеличиваться в размере, используя конструктор копирования A( const A&)
для создания новых копий элементов в векторе.
Могу ли я каким-то образом добиться, чтобы A
вместо этого использовался конструктор перемещения класса ?
Ответы:
Вам нужно сообщить 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 ++ Move.
Благодарим Бо, который намекнул, что это, возможно, связано с исключениями. Также примите во внимание совет Керрека С.Б. и используйте его,
emplace_back
когда это возможно. Он может быть быстрее (но часто нет), он может быть более понятным и компактным, но есть и некоторые подводные камни (особенно с неявными конструкторами).Редактировать , часто по умолчанию это то, что вы хотите: переместите все, что можно переместить, скопируйте остальное. Чтобы явно попросить об этом, напишите
A(A && rhs) = default;
При этом вы получите noexcept, когда это возможно: определен ли конструктор Move по умолчанию как noexcept?
Обратите внимание, что ранние версии Visual Studio 2015 и более ранние не поддерживали это, хотя и поддерживали семантику перемещения.
источник
value_type
«s движение к т е р этоnoexcept
? Возможно, язык ограничивает набор кандидатов на вызов функции, если область вызова также являетсяnoexcept
функцией?noexcept
конструктор перемещения.is_nothrow_move_constructible
будет истинным, если естьnothrow
конструктор копирования. Я не знаю ни одного реального случая использования дорогостоящихnothrow
конструкторов копирования, поэтому неясно, действительно ли это имеет значение.noexcept
как в заголовке, так и в реализации, и когда я выполняю push_back (std:; move), он по-прежнему вызывает конструктор копирования. Я рву волосы здесь.std::move()
неправильныйpush_back()
вызов. Один из тех случаев, когда вы так усердно ищете проблему, что не видите очевидную ошибку прямо перед собой. А потом наступил обеденный перерыв, и я забыл удалить свой комментарий.Интересно, что вектор 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(); }
Это выводит ожидаемый:
Однако, когда я удаляю
noexcept
из~foo()
, результат другой:Думаю, это тоже отвечает на этот вопрос .
источник
Похоже, что единственный способ (для C ++ 17 и более ранних версий) принудительно
std::vector
использовать семантику перемещения при перераспределении - это удалить конструктор копирования :). Таким образом, он будет использовать ваши конструкторы перемещения или умереть, пытаясь во время компиляции :).Есть много правил, по которым
std::vector
НЕ ДОЛЖНО использовать конструктор перемещения при перераспределении, но ничего не говорится о том, где он ДОЛЖЕН его ИСПОЛЬЗОВАТЬ .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;
источник