Est std::unique_ptr<T> requis connaître la définition complète de T?
J'ai un peu de code dans un en-tête qui ressemble à ceci:
#include <memory>
class Thing;
class MyClass
{
std::unique_ptr< Thing > my_thing;
};
Si je comprend cet en-tête dans un rpc qui ne comprennent pas le Thing
de la définition de type, alors ce ne compile pas sous VS2010-SP1:
1>C:\Program Files (x86)\Microsoft
Visual Studio
10.0\VC\include\memory(2067): erreur C2027: utilisation de type non défini "Chose"
Remplacer std::unique_ptr
par std::shared_ptr
et il compile.
Donc, je suppose que c'est l'actuel VS2010 std::unique_ptr
mise en œuvre, qui nécessite la définition complète et il est totalement mise en œuvre-dépendant.
Ou est-il? Est-il quelque chose dans les exigences de la norme qui rend impossible pour std::unique_ptr
mise en œuvre, de travailler à l'avant d'une déclaration? C'est étrange comme il se doit seulement contenir un pointeur à Thing
, n'est-ce pas?
- La meilleure explication de quand vous faites et n'ont pas besoin d'un type complètes avec le C++0x pointeurs intelligents est Howard Hinnant du "Incomplet types et
shared_ptr
/unique_ptr
" le tableau à La fin devrait répondre à votre question. - Merci pour le pointeur de James. J'avais oublié où j'ai mis la table! 🙂
- stackoverflow.com/a/49187113/4361073
- Le lien pour Howard Hinnant du site est en panne. Voici la web.archive.org version de lui. En tout cas, il a répondu parfaitement ci-dessous avec le même contenu 🙂
- Une autre bonne explication est donnée dans l'Article 22 de Scott Meyers moderne et Efficace de C++.
Vous devez vous connecter pour publier un commentaire.
Adopté à partir de ici.
La plupart des modèles dans la norme C++ de la bibliothèque doivent être instancié avec une complète types. Cependant
shared_ptr
etunique_ptr
sont partielle exceptions. Certains, mais pas tous de leurs membres peut être instancié avec incomplètes types. La motivation pour ce est de soutenir les expressions idiomatiques telles que pimpl à l'aide de pointeurs intelligents, et sans risque d'un comportement indéfini.Comportement indéfini peut se produire lorsque vous avez un type incomplète et que vous appelez
delete
sur elle:Ci-dessus est le code légal. Compiler. Votre compilateur peut ou ne peut pas émettre une alerte de code ci-dessus comme ci-dessus. Lorsqu'il s'exécute, les mauvaises choses qui va probablement se produire. Si vous êtes très chanceux, votre programme va planter. Cependant, plus la probabilité d'un résultat, c'est que votre programme en silence à une fuite de mémoire comme
~A()
ne sera pas appelé.À l'aide de
auto_ptr<A>
dans l'exemple ci-dessus n'aide pas. Vous obtenez toujours le même comportement indéfini comme si vous aviez utilisé un pointeur brut.Néanmoins, l'utilisation incomplète des classes dans certains endroits est très utile! C'est là que
shared_ptr
etunique_ptr
de l'aide. L'utilisation de l'un de ces pointeurs intelligents permettra de vous sortir avec un type incomplète, sauf lorsqu'il est nécessaire d'avoir un type complètes. Et le plus important, quand il est nécessaire d'avoir un type complètes, vous obtiendrez une erreur de compilation si vous essayez d'utiliser le pointeur intelligent avec un type incomplète à ce point.Non plus un comportement indéfini:
Si votre code compile, alors vous avez utilisé un type complètes partout où vous en avez besoin.
shared_ptr
etunique_ptr
exiger un type dans des endroits différents. Les raisons sont obscures, d'avoir à faire avec une dynamique deleter vs statique deleter. Les raisons précises ne sont pas importantes. En fait, dans la plupart de code, il n'est pas vraiment important pour vous de savoir exactement où une complète est requise. Juste le code, et si vous vous trompez, le compilateur va vous le dire.Toutefois, dans le cas où il est utile pour vous, voici un tableau qui fait état de plusieurs membres de
shared_ptr
etunique_ptr
à l'égard de l'exhaustivité des exigences. Si le membre exige une complète type, puis l'entrée a un "C", sinon l'entrée de la table est remplie avec "je".Toutes les opérations nécessitant pointeur conversions doivent types pour les deux
unique_ptr
etshared_ptr
.La
unique_ptr<A>{A*}
constructeur peut s'en tirer avec une incomplètesA
seulement si le compilateur n'est pas nécessaire pour mettre en place un appel à~unique_ptr<A>()
. Par exemple, si vous mettez leunique_ptr
sur le tas, vous pouvez vous en sortir avec un incomplètesA
. Plus de détails sur ce point peut être trouvé dans BarryTheHatchet de l' réponse ici.unique_ptr
pour pimpl. Vous avez juste besoin d'un aperçu de votre spéciale des membres. Et c'est sûr que si vous avez accidentellement oublier, le compilateur va vous rappeler. Vous n'aurez pas accidentellement dans l'indéfini de l'exécution de comportement que vous pourriez le faire avecauto_ptr
en C++98/03.[unique.ptr]/5
pourunique_ptr
et[util.smartptr.shared]/2
shared_ptr
.unique_ptr
doit avoir un type complètes où il appelledefault_delete::operator()
en raison de[unique.ptr.dltr.dflt]/4
.unique_ptr
's du constructeur se termine également par uneC
dans ce tableau. Voir ici.unique_ptr
de telle manière que le compilateur n'a pas mis en place d'un appel au destructeur, leT*
constructeur peut fonctionner avec un incomplètesT
. Un exemple serait de mettre leunique_ptr
sur le tas.unique_ptr
comme une variable membre d'une classe, juste explicitement déclarer un destructeur (et constructeur) dans la déclaration de classe (dans le fichier d'en-tête) et passez à l' définir dans le fichier source (et de mettre l'en-tête avec la déclaration complète de la pointe-à la classe dans le fichier source) pour empêcher le compilateur de l'auto-inline le constructeur ou le destructeur dans le fichier d'en-tête (ce qui déclenche le message d'erreur). stackoverflow.com/a/13414884/368896 permet également de me rappeler de cela.Le compilateur a besoin de la définition de la Chose pour générer le destructeur par défaut pour MyClass. Si vous déclarez explicitement le destructeur et de déplacer son (vide) de la mise en œuvre de la RPC fichier, le code doit compiler.
MyClass::~MyClass() = default;
dans la mise en œuvre de fichier semble moins susceptible d'être supprimé par inadvertance plus tard en bas de la route par quelqu'un qui assume le destuctor corps a été effacé plutôt que délibérément laissée en blanc.default
ed etdelete
d les membres de la classe.MyClass::~MyClass() = default
ne pas le déplacer dans la mise en œuvre de fichier sur Clang. (pas encore?)Ce n'est pas dépendant de l'implémentation. La raison qu'il fonctionne, c'est parce que
shared_ptr
détermine le bon destructeur de l'appeler au moment de l'exécution - elle ne fait pas partie de la signature de type. Cependant,unique_ptr
's destructeur est partie de son type, et il doit être connu au moment de la compilation.Il ressemble actuel réponses ne sont pas exactement le clouer pourquoi constructeur par défaut (ou le destructeur) est le problème, mais vide ceux déclarés dans le rpc n'est pas.
Voici ce qui se passe:
Si l'extérieur de la classe (c'est à dire MyClass) ne possède pas de constructeur ou un destructeur alors compilateur génère par défaut. Le problème, c'est que le compilateur insère essentiellement le vide par défaut constructeur/destructeur dans le .php fichier. Cela signifie que le code de défaut constructeur/destructeur sera compilé avec l'hôte de l'exécutable binaire, pas avec votre bibliothèque de fichiers binaires. Cependant, cette définition peut pas vraiment construire les classes partielles. Ainsi, lorsque l'éditeur de liens se passe dans votre bibliothèque, en binaire, et tente d'obtenir constructeur/destructeur, il ne trouve pas tout et vous obtenez l'erreur. Si le constructeur/destructeur de code dans votre .rpc ensuite votre bibliothèque binaire est disponible que pour les liens.
Ce n'est rien à faire avec l'aide de unique_ptr ou shared_ptr et les autres réponses semble être possible confusion bug dans le vieux-VC++ pour unique_ptr de mise en œuvre (VC++ 2015 fonctionne très bien sur ma machine).
Donc morale de l'histoire est que votre tête doit rester libre de tout constructeur/destructeur de définition. Il ne peut contenir de leur déclaration. Par exemple,
~MyClass()=default;
en php ne fonctionne pas. Si vous le permettez compilateur insérer par défaut, le constructeur ou le destructeur, vous obtiendrez une erreur de l'éditeur de liens.Un autre côté remarque: Si vous obtenez toujours cette erreur même après que vous avez constructeur et le destructeur dans un fichier cpp est alors probable que la raison en est que votre bibliothèque n'est pas compilé correctement. Par exemple, une fois, j'ai simplement changé de type de projet à partir de la Console à la Bibliothèque dans VC++ et j'ai eu cette erreur parce que VC++ n'a pas encore ajouté _LIB symbole de préprocesseur et qui produit exactement le même message d'erreur.
Juste pour être complet:
En-tête: A. h
Source A.cpp:
La définition de la classe B doit être vu par un constructeur, un destructeur et tout ce qui peut implicitement supprimer B.
(Bien que le constructeur n'apparaît pas dans la liste ci-dessus, dans VS2017 même le constructeur a besoin de la définition de B. Et cela fait sens lorsque l'on considère que dans le cas d'une exception dans le constructeur de la unique_ptr est à nouveau détruit.)
La définition complète de la Chose est nécessaire au moment de l'instanciation d'un modèle. C'est la raison exacte pour laquelle le pimpl idiome compile.
Si ce n'était pas possible, les gens ne seraient pas poser des questions comme cette.
La réponse la plus simple est juste de l'utilisation shared_ptr à la place.
Comme pour moi,
Il suffit d'inclure l'en-tête ...