web-dev-qa-db-fra.com

Pourquoi utiliser std :: make_unique en C ++ 17?

Pour autant que je sache, C++ 14 a introduit std::make_unique parce que l'ordre d'évaluation des paramètres n'étant pas spécifié, il s'agissait d'un problème de sécurité:

f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A

(Explication: si l'évaluation alloue d'abord la mémoire pour le pointeur brut, puis appelle g() et une exception est levée avant la construction std::unique_ptr, la mémoire est alors perdue.)

L'appel de std::make_unique était un moyen de contraindre l'ordre des appels, rendant ainsi les choses sûres:

f(std::make_unique<MyClass>(param), g());             // Syntax B

Depuis lors, C++ 17 a clarifié l'ordre d'évaluation, rendant Syntax A safe aussi, voici donc ma question: existe-t-il encore une raison d'utiliser std::make_unique sur le constructeur de std::unique_ptr en C +? +17? Pouvez-vous donner quelques exemples?

Pour l’instant, la seule raison que je puisse imaginer est que cela permet de taper MyClass une seule fois (en supposant que vous n’ayez pas besoin de vous fier au polymorphisme avec std::unique_ptr<Base>(new Derived(param))). Cependant, cela semble être une raison assez faible, en particulier lorsque std::make_unique ne permet pas de spécifier un suppresseur alors que le constructeur de std::unique_ptr le fait.

Et juste pour être clair, je ne préconise pas de retirer std::make_unique de la bibliothèque standard (le conserver est logique du moins pour la compatibilité ascendante), mais je me demande plutôt s'il existe encore des situations dans lesquelles elle est fortement préférée. à std::unique_ptr

83
Eternal

Vous avez raison, la raison principale a été supprimée. Il y a toujours les directives n'utilisez pas de nouvelles et qu'il s'agit de raisons moins dactylographiques (il n'est pas nécessaire de répéter le type ou d'utiliser le mot new). Certes, ces arguments ne sont pas forts, mais j'aime vraiment ne pas voir new dans mon code.

Aussi, n'oubliez pas de cohérence. Vous devez absolument utiliser make_shared pour que make_unique soit naturel et corresponde au motif. Il est alors trivial de changer std::make_unique<MyClass>(param) en std::make_shared<MyClass>(param) (ou l'inverse) où la syntaxe A nécessite beaucoup plus de réécriture.

64
NathanOliver

make_unique distingue T de T[] et T[N], unique_ptr(new ...) ne le fait pas.

Vous pouvez facilement obtenir un comportement indéfini en passant un pointeur qui était new[]ed à un unique_ptr<T>, ou en passant un pointeur qui était newed à un unique_ptr<T[]>.

44
Caleth

La raison est d'avoir un code plus court sans doublons. Comparer

f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());

Vous enregistrez MyClass, new et les accolades. Cela ne coûte qu'un caractère de plus dans make en comparaison avec ptr.

20
S.M.

Chaque utilisation de new doit faire l'objet d'un audit minutieux pour en assurer l'exactitude à vie; est-il supprimé? Juste une fois?

Chaque utilisation de make_unique ne correspond pas à ces caractéristiques supplémentaires; tant que la durée de vie de l'objet propriétaire est "correcte", le pointeur unique est récursivement "correct".

Maintenant, il est vrai que unique_ptr<Foo>(new Foo()) est identique à tous les égards1 à make_unique<Foo>(); cela nécessite simplement un simple "grep votre code source pour toutes les utilisations de new pour les auditer".


1 en fait un mensonge dans le cas général. Le transfert parfait n'est pas parfait, {}, init par défaut, les tableaux sont tous des exceptions.

18

Depuis lors, C++ 17 a clarifié l'ordre d'évaluation, rendant ainsi Syntax A safe aussi.

Ce n'est vraiment pas suffisant. S'appuyer sur une clause technique récemment introduite en tant que garantie de sécurité n'est pas une pratique très robuste:

  • Quelqu'un pourrait compiler ce code en C++ 14.
  • Vous seriez en train d’encourager l’utilisation de new bruts ailleurs, par exemple. en copiant-collant votre exemple.
  • Comme S.M. suggère, étant donné qu'il y a duplication de code, un type peut être modifié sans que l'autre soit modifié.
  • Une sorte de IDE refactoring automatique pourrait déplacer new ailleurs (ok, d'accord, peu de chance que cela se produise).

En règle générale, il est judicieux que votre code soit approprié/robuste/clairement valide sans recourir à la mise en langage, à la recherche de clauses techniques mineures ou obscures dans la norme.

(c’est essentiellement le même argument que j’ai avancé ici à propos de l’ordre de destruction des tuples.)

0
einpoklum