Supposons que l’espace de nom std
Le projet N3690 du comité C ++ 14 définit std::make_unique
ainsi:
[n3690: 20.9.1.4]:
créationunique_ptr
[unique.ptr.create]
template unique_ptr make_unique(Args&&... args);
- Comment extraire le nom de fichier source sans chemin et sans suffixe lors de la compilation?
- Pourquoi n’y a-t-il pas de fonction std :: allocate_unique en C ++ 14?
- Recherche de nom non qualifiée: Pourquoi la déclaration locale empêche la déclaration d’utiliser la directive
- Peut-on omettre les doubles accolades pour std :: array en C ++ 14?
- Modèle de variable à la scope de la classe
1 Remarques: Cette fonction ne doit pas participer à la résolution de surcharge, sauf si
T
n’est pas un tableau.
2 renvoie:unique_ptr(new T(std::forward(args)...)).
template unique_ptr make_unique(size_t n);
3 Remarques: Cette fonction ne doit pas participer à la résolution de la surcharge sauf si
T
est un tableau de bornes inconnues.
4 renvoie:unique_ptr(new typename remove_extent::type[n]()).
template unspecified make_unique(Args&&...) = delete;
5 Remarques: Cette fonction ne participera pas à la résolution de la surcharge sauf si
T
est un tableau de liaisons connues.
Maintenant, cela me semble être à peu près aussi clair que de la boue et je pense que cela nécessite davantage d’exposition. Mais, mis à part ce commentaire éditorial, je crois avoir décodé le sens de chaque variante:
template unique_ptr make_unique(Args&&... args);
Votre make_unique
standard pour les types non-array. Vraisemblablement, la “remarque” indique qu’une certaine forme d’assertion statique ou astuce SFINAE consiste à empêcher l’instanciation du modèle lorsque T
est un type de tableau.
À un niveau élevé, voyez-le comme le pointeur intelligent équivalent à T* ptr = new T(args);
.
template unique_ptr make_unique(size_t n);
Une variante pour les types de tableau. Crée un tableau alloué de manière dynamic de n
× Ts
et le retourne dans un unique_ptr
.
À un niveau élevé, voyez-le comme le pointeur intelligent équivalent à T* ptr = new T[n];
.
template unspecified make_unique(Args&&...)
Non autorisé. ” non spécifié ” serait probablement unique_ptr
.
Serait autrement le pointeur intelligent équivalent à quelque chose comme T[N]* ptr = new (keep_the_dimension_please) (the_dimension_is_constexpr) T[N];
invalide T[N]* ptr = new (keep_the_dimension_please) (the_dimension_is_constexpr) T[N];
.
Tout d’abord, ai-je raison? Et, si oui, que se passe-t-il avec la troisième fonction?
S’il est possible d’empêcher les programmeurs d’essayer d’allouer de manière dynamic un tableau tout en fournissant des arguments de constructeur pour chaque élément (de même que new int[5](args)
est impossible), cela est déjà couvert par le fait que la première fonction ne peut pas être instanciée pour les types de tableaux, n’est-ce pas?
S’il est là pour empêcher l’ajout au langage d’une construction telle que T[N]* ptr = new T[N]
(où N
est un certain constexpr
), alors, pourquoi? Ne pourrait-il pas exister un unique_ptr
qui enveloppe un bloc alloué de manière dynamic de N
× T
s? Serait-ce une si mauvaise chose, dans la mesure où le comité s’est make_unique
d’empêcher sa création à l’aide de make_unique
?
Pourquoi make_unique
interdit?
Citant la proposition originale :
T[N]
À partir de N3485,
unique_ptr
ne fournit pas de spécialisation partielle pourT[N]
. Cependant, les utilisateurs seront fortement tentés d’écriremake_unique
. Ceci est un scénario sans victoire. Renvoyer() unique_ptr
sélectionnerait le modèle principal d’objects uniques, ce qui est bizarre. Leunique_ptr
serait une exception à la règle par ailleurs ironique quimake_unique
renvoie() unique_ptr
. Par conséquent, cette proposition rendT[N]
mal formé ici, ce qui permet aux implémentations d’émettre des messagesstatic_assert
utiles.
L’auteur de la proposition, Stephan T. Lavavej, illustre cette situation dans cette vidéo sur Core C ++ (avec la permission de chris ), à partir de la minute 1:01:10 (plus ou moins).
Pour moi, la troisième surcharge semble superflue car cela ne change rien au fait que les autres surcharges ne correspondent pas à T[N]
et que cela ne semble pas aider à générer de meilleurs messages d’erreur. Considérez l’implémentation suivante:
template< typename T, typename... Args > typename enable_if< !is_array< T >::value, unique_ptr< T > >::type make_unique( Args&&... args ) { return unique_ptr< T >( new T( forward< Args >( args )... ) ); } template< typename T > typename enable_if< is_array< T >::value && extent< T >::value == 0, unique_ptr< T > >::type make_unique( const size_t n ) { using U = typename remove_extent< T >::type; return unique_ptr< T >( new U[ n ]() ); }
Lorsque vous essayez d’appeler std::make_unique
, le message d’erreur indique que les deux candidats sont désactivés par enable_if
. Si vous ajoutez la troisième surcharge supprimée, le message d’erreur répertorie trois candidats. Aussi, puisqu’il est spécifié comme =delete;
, vous ne pouvez pas fournir de message d’erreur plus significatif dans le corps de la troisième surcharge, par exemple static_assert(sizeof(T)==0,"array of known bound not allowed for std::make_shared");
.
Voici l’ exemple en direct au cas où vous voudriez jouer avec.
Le fait que la troisième surcharge se soit retrouvée dans N3656 et N3797 est probablement dû à l’historique de la façon dont make_unique
été développé au fil du temps, mais je suppose que seule la société STL peut répondre à cette question 🙂