Tengo varios std::vectorobjetos de cierta clase A. La clase no es trivial y tiene constructores de copia y constructores de movimiento definidos.
std::vector<A> myvec;
Si relleno el vector con Aobjetos (usando por ejemplo myvec.push_back(a)), el vector crecerá de tamaño, usando el constructor de copia A( const A&)para instanciar nuevas copias de los elementos en el vector.
¿Puedo hacer cumplir de alguna manera que el constructor de movimiento de la clase se Aesté usando en su lugar?

Respuestas:
Debe informar a C ++ (específicamente
std::vector) que su constructor de movimiento y su destructor no arroja, usandonoexcept. Luego, se llamará al constructor de movimiento cuando el vector crezca.Así es como declarar e implementar un constructor de movimiento que es respetado por
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... }Si el constructor no lo es
noexcept, nostd::vectorpuede usarlo, ya que entonces no puede asegurar las garantías de excepción exigidas por el estándar.Para obtener más información sobre lo que se dice en el estándar, lea C ++ Move semántica y excepciones
Gracias a Bo, quien insinuó que puede tener que ver con excepciones. También considere los consejos de Kerrek SB y utilícelo
emplace_backcuando sea posible. Se puede ser más rápido (pero a menudo no lo es), que puede ser más clara y compacta, pero también hay algunas trampas (especialmente con los constructores no explícitas).Edite , a menudo lo predeterminado es lo que desea: mueva todo lo que se pueda mover, copie el resto. Para pedirlo explícitamente, escriba
A(A && rhs) = default;Al hacer eso, obtendrá noexcepto cuando sea posible: ¿El constructor Move predeterminado está definido como noexcept?
Tenga en cuenta que las versiones anteriores de Visual Studio 2015 y anteriores no lo admitían, aunque admite la semántica de movimiento.
fuente
value_type's movimiento ctor esnoexcept? ¿Quizás el lenguaje restringe el conjunto de candidatos a la llamada de función cuando el alcance de la llamada también es unanoexceptfunción?noexceptconstructor de movimientos.is_nothrow_move_constructibleserá verdadero si hay unnothrowconstructor de copia. No conozco ningún caso real denothrowconstructores de copias costosos, por lo que no está claro que realmente importe.noexcepttanto en el encabezado como en la implementación, y cuando hago un push_back (std:; move) todavía llama al constructor de copia. Me estoy arrancando el pelo aquí.std::move()en lapush_back()llamada equivocada . Uno de esos momentos en los que busca un problema con tanta atención que no ve el error obvio justo frente a usted. Y luego llegó la hora del almuerzo y olvidé borrar mi comentario.Curiosamente, el vector de gcc 4.7.2 solo usa el constructor de movimiento si tanto el constructor de movimiento como el destructor lo son
noexcept. Un simple ejemplo: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(); }Esto da como resultado lo esperado:
Sin embargo, cuando elimino
noexceptde~foo(), el resultado es diferente:Supongo que esto también responde a esta pregunta .
fuente
Parece que la única forma (para C ++ 17 y versiones anteriores) de hacer cumplir la
std::vectorsemántica de uso de movimiento en la reasignación es eliminar el constructor de copia :). De esta manera, usará sus constructores de movimientos o morirá en el intento, en tiempo de compilación :).Hay muchas reglas en las que
std::vectorNO DEBE usar move constructor en la reasignación, pero nada sobre dónde DEBE USARLO .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; };En Vivo
o
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 {}; };Código en vivo
Su
Tclase debe tener unnoexceptconstructor de movimiento / operador de asignación y unnoexceptdestructor. De lo contrario, obtendrá un error de compilación.std::vector<move_only<MyClass>> vec;fuente