Pourquoi n'y a-t-il pas de std::make_unique
modèle de fonction dans la bibliothèque C ++ 11 standard? je trouve
std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));
un peu bavard. Les éléments suivants ne seraient-ils pas beaucoup plus agréables?
auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);
Cela cache new
bien et ne mentionne le type qu'une seule fois.
Quoi qu'il en soit, voici ma tentative d'implémentation de make_unique
:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
Il m'a fallu un certain temps pour obtenir les std::forward
éléments à compiler, mais je ne sais pas si c'est correct. C'est ça? Que std::forward<Args>(args)...
signifie exactement ? Qu'est-ce que le compilateur en fait?
c++
c++11
variadic-templates
unique-ptr
perfect-forwarding
fredoverflow
la source
la source
unique_ptr
prend un deuxième paramètre de modèle que vous devriez en quelque sorte autoriser - c'est différent deshared_ptr
.make_unique
avec un suppresseur personnalisé, car il est évident qu'il alloue via plain oldnew
et doit donc utiliser plain olddelete
:)make_unique
serait limitée à l'new
allocation ... eh bien, c'est bien si vous voulez l'écrire, mais je peux voir pourquoi quelque chose comme ça ne fait pas partie de la norme.make_unique
modèle car le constructeur destd::unique_ptr
est explicite, et donc il est verbeux de revenirunique_ptr
d'une fonction. Aussi, je préfère utiliserauto p = make_unique<foo>(bar, baz)
questd::unique_ptr<foo> p(new foo(bar, baz))
.make_unique
arriveC++14
, voir isocpp.org/blog/2013/04/trip-report-iso-c-spring-2013-meetingRéponses:
Herb Sutter, président du comité de normalisation C ++, écrit sur son blog :
Il donne également une implémentation identique à celle donnée par le PO.
Edit: fait
std::make_unique
maintenant partie de C ++ 14 .la source
make_unique
modèle de fonction ne garantit pas lui-même les appels sécurisés d'exception. Il s'appuie sur la convention, que l'appelant l'utilise. En revanche, la vérification stricte des types statiques (qui est la principale différence entre C ++ et C) repose sur l'idée d' appliquer la sécurité, via les types. Et pour cela,make_unique
peut simplement être une classe au lieu d'une fonction. Par exemple, consultez mon article de blog de mai 2010. Il est également lié à la discussion sur le blog de Herb.make_unique
une classe, je pense maintenant qu'il vaut mieux en faire une fonction qui produit unmake_unique_t
, la raison en est un problème avec l'analyse la plus perverse :-).make_unique
offre la garantie d'exception forte. Ou peut-être que vous mélangez l'outil avec son utilisation, auquel cas aucune fonction n'est sûre d'exception. Considérezvoid f( int *, int* ){}
, offre clairement lano throw
garantie, mais selon votre raisonnement, il n'est pas exceptionnellement sûr, car il peut être utilisé à mauvais escient. Pire, cevoid f( int, int ) {}
n'est pas une exception non plus!:typedef unique_ptr<int> up; f( *up(new int(5)), *up(new int(10)))
...make_unique
œuvre comme ci-dessus et vous me dirigez vers un article de Sutter, l'article que mon google-fu m'a indiqué aux États quimake_unique
offre la garantie d'exception forte , ce qui contredit votre déclaration ci-dessus. Si vous avez un article différent, je suis intéressé à le lire. Donc, ma question d'origine est la suivante: commentmake_unique
(tel que défini ci-dessus) n'est-il pas une exception sûre? (Sidenote: oui, je pense que celamake_unique
améliore la sécurité des exceptions dans les endroits où il peut être appliqué)make_unique
. Tout d'abord, je ne vois pas en quoi celui-ci est dangereux, et je ne vois pas comment l'ajout d'un type supplémentaire le rendrait plus sûr . Ce que je sais, c'est que je suis intéressé à comprendre les problèmes que cette mise en œuvre peut avoir - je ne vois aucun - et comment une autre mise en œuvre pourrait les résoudre. Quelles sont les conventions quimake_unique
dépend de? Comment utiliseriez-vous la vérification de type pour renforcer la sécurité? Ce sont les deux questions pour lesquelles j'aimerais une réponse.Bien, mais Stephan T. Lavavej (mieux connu sous le nom de STL) a une meilleure solution pour
make_unique
, qui fonctionne correctement pour la version tableau.Cela peut être vu sur sa vidéo Core C ++ 6 .
Une version mise à jour de la version STL de make_unique est désormais disponible sous le numéro N3656 . Cette version a été adoptée dans le projet C ++ 14.
la source
make_unique
devrait aller dans un en-tête. Les en-têtes ne doivent pas importer un espace de noms (voir l'article n ° 59 du livre "C ++ Coding Standards" de Sutter / Alexandrescu). Les changements de Xeo permettent d'éviter d'encourager les mauvaises pratiques.std::make_shared
n'est pas seulement un raccourci pourstd::shared_ptr<Type> ptr(new Type(...));
. Il fait quelque chose que vous ne pouvez pas faire sans lui.Afin de faire son travail,
std::shared_ptr
doit allouer un bloc de suivi en plus de conserver le stockage pour le pointeur réel. Cependant, comme ilstd::make_shared
alloue l'objet réel, il est possible qu'ilstd::make_shared
alloue à la fois l'objet et le bloc de suivi dans le même bloc de mémoire.Donc, alors
std::shared_ptr<Type> ptr = new Type(...);
qu'il y aurait deux allocations de mémoire (une pour lenew
, une dans lestd::shared_ptr
bloc de suivi),std::make_shared<Type>(...)
allouerait une bloc de mémoire.C'est important pour de nombreux utilisateurs potentiels de
std::shared_ptr
. La seule chose àstd::make_unique
faire serait d'être un peu plus pratique. Rien de plus que ça.la source
Bien que rien ne vous empêche d'écrire votre propre assistant, je pense que la principale raison de fournir
make_shared<T>
dans la bibliothèque est qu'il crée en fait un type de pointeur partagé différent de celuishared_ptr<T>(new T)
qui est différemment alloué, et il n'y a aucun moyen d'y parvenir sans le dédié assistant.VotreCorrection: ce n'est pas vrai en fait: avoir un appel de fonction pour encapsuler l'make_unique
emballage, d'autre part, n'est que du sucre syntaxique autour d'unenew
expression, alors même s'il peut sembler agréable à l'œil, il n'apporte riennew
à la table.new
expression offre une sécurité d'exception, par exemple dans le cas où vous appelez une fonctionvoid f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)
. Avoir deuxnew
s bruts non séquencés l'un par rapport à l'autre signifie que si une nouvelle expression échoue avec une exception, l'autre peut entraîner une fuite de ressources. Quant à savoir pourquoi il n'y a pasmake_unique
dans la norme: c'était juste oublié. (Cela se produit occasionnellement. Il n'y a pas non plus destd::cbegin
dans la norme même s'il devrait y en avoir un.)Notez également que
unique_ptr
prend un deuxième paramètre de modèle que vous devriez en quelque sorte autoriser; ceci est différent deshared_ptr
, qui utilise l'effacement de type pour stocker des suppresseurs personnalisés sans les intégrer au type.la source
shared_ptr<T>(new T)
utilise l'un d'eux,make_shared<T>()
utilise un autre. Permettre que c'est une bonne chose, et la version partagée est en quelque sorte le pointeur partagé le plus léger que vous puissiez obtenir.shared_ptr
alloue un bloc de mémoire dynamique pour maintenir le décompte et l'action "broyeur" lorsque vous créez unshared_ptr
. Si vous passez le pointeur explicitement, il doit créer un "nouveau" bloc, si vous l'utilisez, vousmake_shared
pouvez regrouper votre objet et les données satellite dans un seul bloc de mémoire (unnew
), ce qui entraîne une allocation / désallocation plus rapide, moins de fragmentation et (normalement ) un meilleur comportement du cache.En C ++ 11
...
est également utilisé (dans le code modèle) pour "l'expansion du pack".L'exigence est que vous l'utilisiez comme suffixe d'une expression contenant un pack de paramètres non développé, et il appliquera simplement l'expression à chacun des éléments du pack.
Par exemple, en s'appuyant sur votre exemple:
Ce dernier étant incorrect je pense.
De plus, un pack d'arguments ne peut pas être passé à une fonction non développée. Je ne suis pas sûr d'un pack de paramètres de modèle.
la source
std::forward<Args>(args)...
, qui se développe enforward<T1>(x1), forward<T2>(x2), ...
.forward
qu'en fait, il faut toujours un paramètre de modèle, non?Inspiré par l'implémentation de Stephan T. Lavavej, j'ai pensé qu'il serait bien d'avoir une make_unique qui supporte les extensions de tableau, c'est sur github et j'aimerais avoir des commentaires à ce sujet. Cela vous permet de faire ceci:
la source