sociologia sobre psicologia memes meme los investigacion introducción funciones estudio articulos c++ c++11 move-constructor

c++ - sobre - ¿Cómo puedo verificar si un constructor de movimiento se está generando implícitamente?



memes pdf (3)

  1. inhabilitar inlining ( -fno-inline )
  2. ya sea
    • asegúrese de que el constructor puede usar un constructor de movimiento, o (mejor)
    • agregue temporalmente una llamada a std::move(MyStruct) en cualquier lugar del código compilado para cumplir con el requisito de uso de ODR
  3. ya sea
    • asegúrese de que MyStruct tenga al menos una clase principal o un miembro no estático (recursivamente), con un constructor de movimientos no trivial (por ejemplo, una std::string sería suficiente), o (más fácil)
    • Agregue temporalmente un miembro std :: string a su clase
  4. compile / vincule y ejecute el archivo de objeto resultante a través de nm -C ... | grep ''MyStruct.*&&'' nm -C ... | grep ''MyStruct.*&&''

El resultado implicará si el constructor de movimiento fue generado o no.

Como se discutió en la pregunta en sí, este método no parecía funcionar de manera confiable, pero después de solucionar los dos problemas que lo hacían poco confiable: en línea y la trivialidad del constructor de movimientos , resultó ser un método de trabajo.

Si el constructor de movimiento generado está implícito o explícitamente no tiene ningún rol, ya sea que el valor predeterminado sea trivial o no sea ​​relevante: un constructor trivial de movimiento (y copia) simplemente realizará una copia de byte-byo del objeto.

Tengo varias clases para las que deseo verificar si se está generando un constructor de movimiento predeterminado. ¿Hay alguna forma de verificar esto (ya sea una aserción en tiempo de compilación, analizar los archivos de objetos generados u otra cosa)?

Ejemplo motivacional:

class MyStruct : public ComplicatedBaseClass { std::vector<std::string> foo; // possibly huge ComplicatedSubObject bar; };

Si algún miembro de cualquier base o miembro de cualquiera de las Complicated...Object clases de Complicated...Object no se pueden mover, MyStruct no tendrá su constructor implícito de movimiento generado y, por lo tanto, no podrá optimizar el trabajo de copia de foo , cuando se pueda realizar un movimiento, aunque foo es movible.

Deseo evitar:

  1. comprobación tediosa de las condiciones para la generación implícita de ctor de movimiento ,
  2. predeterminar explícita y recursivamente las funciones miembro especiales de todas las clases afectadas, sus bases y sus miembros, solo para asegurarse de que haya un constructor de movimientos disponible.

Ya he intentado lo siguiente y no funcionan:

  1. use std::move explícitamente: esto invocará el ctor de copia si no hay un ctor de movimiento disponible.
  2. use std::is_move_constructible tendrá éxito cuando haya un constructor de copia que acepte el const Type& , que se genera de forma predeterminada ( siempre que el constructor de movimiento no se elimine explícitamente, al menos ).
  3. use nm -C para verificar la presencia de movimiento ctor [ver más abajo]. Sin embargo, un enfoque alternativo es viable [ver respuesta].

Intenté mirar los símbolos generados de una clase trivial como esta:

#include <utility> struct MyStruct { MyStruct(int x) : x(x) {} //MyStruct(const MyStruct& rhs) : x(rhs.x) {} //MyStruct(MyStruct&& rhs) : x(rhs.x) {} int x; }; int main() { MyStruct s1(4); MyStruct s2(s1); MyStruct s3(std::move(s1)); return s1.x+s2.x+s3.x; // make sure nothing is optimized away }

Los símbolos generados se ven así:

$ CXXFLAGS="-std=gnu++11 -O0" make -B x; ./x; echo $?; nm -C x | grep MyStruct | cut -d'' '' -f3,4,5 g++ -std=gnu++11 -O0 x.cc -o x 12 .pdata$_ZN8MyStructC1Ei .pdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_ .text$_ZN8MyStructC1Ei .text$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_ .xdata$_ZN8MyStructC1Ei .xdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_ MyStruct::MyStruct(int) std::remove_reference<MyStruct&>::type&&

La salida es la misma cuando omito explícitamente la copia y muevo los ctors (sin símbolos).

Con mis propios controladores de copiar y mover, la salida se ve así:

$ vim x.cc; CXXFLAGS="-std=gnu++11 -O0" make -B x; ./x; echo $?; nm -C x | grep MyStruct | cut -d'' '' -f3,4,5 g++ -std=gnu++11 -O0 x.cc -o x 12 .pdata$_ZN8MyStructC1Ei .pdata$_ZN8MyStructC1EOKS_ .pdata$_ZN8MyStructC1ERKS_ .pdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_ .text$_ZN8MyStructC1Ei .text$_ZN8MyStructC1EOKS_ .text$_ZN8MyStructC1ERKS_ .text$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_ .xdata$_ZN8MyStructC1Ei .xdata$_ZN8MyStructC1EOKS_ .xdata$_ZN8MyStructC1ERKS_ .xdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_ MyStruct::MyStruct(int) MyStruct::MyStruct(MyStruct&&) MyStruct::MyStruct(MyStruct const&) std::remove_reference<MyStruct&>::type&& std::move<MyStruct&>(MyStruct&)

Así que parece que este enfoque tampoco funciona.

Sin embargo, si la clase de destino tiene un miembro con constructor de movimiento explícito, el constructor de movimiento generado implícitamente será visible para la clase de destino. Es decir, con este código:

#include <utility> struct Foobar { Foobar() = default; Foobar(const Foobar&) = default; Foobar(Foobar&&) {} }; struct MyStruct { MyStruct(int x) : x(x) {} int x; Foobar f; }; int main() { MyStruct s1(4); MyStruct s2(s1); MyStruct s3(std::move(s1)); return s1.x+s2.x+s3.x; // make sure nothing is optimized away }

MyStruct el símbolo para el ctor de movimiento de MyStruct, pero no el ctor de copia, ya que parece estar totalmente implícito. Supongo que el compilador genera un ctor trivial de movimientos en línea si puede, y uno no trivial si debe llamar a otros ctors de movimientos no triviales. Aunque esto todavía no me ayuda con mi búsqueda.


Como señaló Yakk, a menudo no es relevante si es un compilador generado o no.

Puedes verificar si un tipo es trivial o no se puede mover mover construible

template< class T > struct is_trivially_move_constructible; template< class T > struct is_nothrow_move_constructible;

http://en.cppreference.com/w/cpp/types/is_move_constructible

Limitación; También permite la construcción de copia trivial / nothrow.


Declare las funciones de miembro especiales que desea que existan en MyStruct , pero no MyStruct las que desea verificar. Supongamos que le interesan las funciones de movimiento y también quiere asegurarse de que el constructor de movimiento sea noexcept :

struct MyStruct { MyStruct() = default; MyStruct(const MyStruct&) = default; MyStruct(MyStruct&&) noexcept; // no = default; here MyStruct& operator=(const MyStruct&) = default; MyStruct& operator=(MyStruct&&); // or here };

Luego explícitamente los predeterminan, fuera de la definición de la clase:

inline MyStruct::MyStruct(MyStruct&&) noexcept = default; inline MyStruct& MyStruct::operator=(MyStruct&&) = default;

Esto desencadena un error en tiempo de compilación si la función predeterminada se definirá implícitamente como eliminada.