J’ai plusieurs classes pour lesquelles je souhaite vérifier si un constructeur de déplacement par défaut est généré. Existe-t-il un moyen de vérifier cela (qu’il s’agisse d’une assertion au moment de la compilation, d’une parsing des fichiers object générés ou de quelque chose d’autre)?
Exemple de motivation:
class MyStruct : public ComplicatedBaseClass { std::vector foo; // possibly huge ComplicatedSubObject bar; };
Si un membre d’une base ou un membre d’une classe d’ Complicated...Object
ne peut pas être déplacé, MyStruct
n’aura pas son constructeur de déplacement implicite, et risque donc de ne pas optimiser le travail de copie de foo
, lorsqu’un déplacement peut être effectué. même si foo
est mobile.
Je souhaite éviter:
J’ai déjà essayé ce qui suit et ils ne fonctionnent pas:
std::move
– ceci invoquera le ctor de copie si aucun ctor de move n’est disponible. std::is_move_constructible
réussira s’il existe un constructeur de copie acceptant const Type&
, qui est généré par défaut ( tant que le constructeur de déplacement n’est pas explicitement supprimé, au moins ). nm -C
pour vérifier la présence de move ctor [voir ci-dessous]. Cependant, une approche alternative est viable [voir la réponse]. J’ai essayé de regarder les symboles générés d’une classe sortingviale comme ceci:
#include 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 }
Les symboles générés ressemblent à ceci:
$ 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 -ox 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::type&&
La sortie est la même lorsque je copie explicitement par défaut la copie et que je déplace les ctors (pas de symboles).
Avec ma propre copie et déplacer des facteurs, la sortie ressemble à ceci:
$ 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 -ox 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::type&& std::move(MyStruct&)
Il semble donc que cette approche ne fonctionne pas non plus.
Cependant, si la classe cible a un membre avec un constructeur de déplacement explicite, le constructeur de déplacement généré implicitement sera visible pour la classe cible. C’est à dire avec ce code:
#include 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
le symbole du symbole de déplacement de MyStruct, mais pas celui de la copie, car il semble être totalement implicite. Je suppose que le compilateur génère un ctor de mouvement en ligne sortingvial s’il le peut et un non-sortingvial s’il doit appeler d’autres ctors de mouvement non-sortingviaux. Cela ne m’aide toujours pas dans ma quête.
Déclarez les fonctions de membre spéciales que vous voulez exister dans MyStruct
, mais ne MyStruct
pas par défaut celles que vous voulez vérifier. Supposons que vous vous souciez des fonctions de déplacement et que vous souhaitiez également vous assurer que le constructeur du déplacement est 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 };
Puis explicitement par défaut, en dehors de la définition de classe:
inline MyStruct::MyStruct(MyStruct&&) noexcept = default; inline MyStruct& MyStruct::operator=(MyStruct&&) = default;
Cela déclenche une erreur au moment de la compilation si la fonction par défaut est implicitement définie comme étant supprimée.
Comme Yakk l’a fait remarquer, ce n’est souvent pas pertinent que le compilateur soit généré ou non.
Vous pouvez vérifier si un type est sortingvial ou si aucun mouvement n’est constructible
template< class T > struct is_sortingvially_move_constructible; template< class T > struct is_nothrow_move_constructible;
http://en.cppreference.com/w/cpp/types/is_move_constructible
Limitation; il permet également la construction de copies sortingviales / à mémoriser.
-fno-inline
) std::move(MyStruct)
n’importe où dans le code compilé pour répondre à l’exigence odr-used MyStruct
a au moins une classe parent ou un membre non statique (de manière récursive), avec un constructeur de mouvement non sortingvial (par exemple, un std::ssortingng
suffirait), ou (plus facilement) nm -C ... | grep 'MyStruct.*&&'
nm -C ... | grep 'MyStruct.*&&'
Le résultat impliquera si le constructeur de déplacement a été généré ou non.
Comme indiqué dans la question elle-même, cette méthode ne semblait pas fonctionner de manière fiable, mais après avoir résolu les deux problèmes qui la rendaient peu fiable: inline et sortingviality du constructeur de déménagement , elle s’est révélée être une méthode de travail.
Que le constructeur de déplacement généré soit implicitement ou explicitement défini par défaut ne joue aucun rôle – que le choix par défaut soit sortingvial ou non soit pertinent: un constructeur de déplacement (et de copie) sortingvial effectuera simplement une copie octet par pouce de l’object.