274 votes

Est std::unique_ptr<T> doivent connaître la définition complète de T ?</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 comprend pas l' 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 qu'il compile.

Donc, je suppose que c'est l'actuel VS2010 std::unique_ptr's la 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's la mise en œuvre de travailler avec une déclaration anticipée? C'est étrange comme il se doit seulement contenir un pointeur à l' Thing, n'est-ce pas?

358voto

Howard Hinnant Points 59526

Adoptée à partir d' ici.

La plupart des modèles dans la norme C++ de la bibliothèque doivent être instancié avec une complète types. Toutefois shared_ptr et unique_ptr sont partielle des 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.

Un comportement indéfini peut se produire lorsque vous avez un type incomplète et que vous appelez delete sur:

class A;
A* a = ...;
delete a;

Le 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 en tant que ~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 et unique_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.

Pas plus d'un comportement indéfini:

Si votre code compile, alors vous avez utilisé un type complètes partout où vous en avez besoin.

class A
{
    class impl;
    std::unique_ptr<impl> ptr_;  // ok!

public:
    A();
    ~A();
    // ...
};

shared_ptr et unique_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 l' shared_ptr et unique_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".

Complete type requirements for unique_ptr and shared_ptr

                            unique_ptr       shared_ptr
+------------------------+---------------+---------------+
|          P()           |      I        |      I        |
|  default constructor   |               |               |
+------------------------+---------------+---------------+
|      P(const P&)       |     N/A       |      I        |
|    copy constructor    |               |               |
+------------------------+---------------+---------------+
|         P(P&&)         |      I        |      I        |
|    move constructor    |               |               |
+------------------------+---------------+---------------+
|         ~P()           |      C        |      I        |
|       destructor       |               |               |
+------------------------+---------------+---------------+
|         P(A*)          |      I        |      C        |
+------------------------+---------------+---------------+
|  operator=(const P&)   |     N/A       |      I        |
|    copy assignment     |               |               |
+------------------------+---------------+---------------+
|    operator=(P&&)      |      C        |      I        |
|    move assignment     |               |               |
+------------------------+---------------+---------------+
|        reset()         |      C        |      I        |
+------------------------+---------------+---------------+
|       reset(A*)        |      C        |      C        |
+------------------------+---------------+---------------+

Toutes les opérations nécessitant pointeur conversions doivent types pour les deux unique_ptr et shared_ptr.

48voto

Igor Nazarenko Points 1389

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 déplacer sa mise en œuvre (vide) vers le fichier CPP, le code doit compiler.

19voto

Puppy Points 90818

Ce n’est pas dépendant de l’implémentation. La raison que cela fonctionne est que détermine le bon destructeur d’appeler chez run-time-it ne fait pas partie de la signature de type. Toutefois, de partie de est destructeur de ce type et il doit être connu au moment de la compilation.

1voto

BЈовић Points 28674

La définition complète de la chose est requise au moment de l’instanciation du modèle. C’est la raison exacte, pourquoi l’idiome pimpl compile.

Si ce n’était pas possible, les gens ne seraient pas poser des questions comme ce.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X