Pourquoi n'y a-t-il pas de modèle de fonction std::make_unique
Dans la bibliothèque standard C++ 11? je trouve
std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));
un peu prolixe. Ce qui suit ne serait-il pas beaucoup plus agréable?
auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);
Ceci cache joliment le new
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 bon bout de temps pour compiler le fichier std::forward
, Mais je ne sais pas si c'est correct. Est-ce Que signifie exactement std::forward<Args>(args)...
? Qu'est-ce que le compilateur en fait?
Herb Sutter, président du comité de normalisation C++, écrit sur son blog :
Ce C++ 11 n’inclut pas
make_unique
est en partie un oubli, et il sera presque certainement ajouté à l'avenir.
Il donne également une implémentation identique à celle donnée par l'OP.
Modifier: std::make_unique
fait maintenant partie de C++ 14 .
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.
#include <memory>
#include <type_traits>
#include <utility>
template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::false_type, Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args&&... args) {
static_assert(std::extent<T>::value == 0,
"make_unique<T[N]>() is forbidden, please use make_unique<T[]>().");
typedef typename std::remove_extent<T>::type U;
return std::unique_ptr<T>(new U[sizeof...(Args)]{std::forward<Args>(args)...});
}
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args) {
return make_unique_helper<T>(std::is_array<T>(), std::forward<Args>(args)...);
}
Ceci peut être vu sur sa vidéo Core C++ 6 .
Une version mise à jour de la version STL de make_unique est maintenant disponible sous le nom N3656 . Cette version a été adoptée dans le projet de C++ 14.
Bien que rien ne vous empêche d'écrire votre propre aide, je pense que la raison principale de fournir make_shared<T>
Dans la bibliothèque est qu'elle crée en réalité un type interne de pointeur partagé différent de shared_ptr<T>(new T)
, qui est alloué différemment, et il n’ya aucun moyen d’y parvenir sans l’assistant dédié.
En revanche, votre wrapper Correction: ce n'est pas vrai: avoir un appel de fonction pour envelopper l'expression make_unique
N’est que du sucre syntaxique autour d’une expression new
. Ainsi, même s’il peut paraître agréable à regarder, il n’apporte rien new
à la table.new
fournit une sécurité d'exception, par exemple dans le cas où vous appelez une fonction void f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)
. Avoir deux new
s brutes non ordonnées l'une par rapport à l'autre signifie que si une nouvelle expression échoue avec une exception, l'autre risque de perdre des ressources. Quant à savoir pourquoi il n’ya pas de make_unique
Dans la norme: c’était juste oublié. (Cela arrive occasionnellement. Il n'y a pas non plus de std::cbegin
Global dans la norme, même s'il devrait en exister un.)
Notez également que unique_ptr
Prend un deuxième paramètre de modèle que vous devriez en quelque sorte permettre; ceci diffère de shared_ptr
, qui utilise l'effacement de type dans enregistre les deleters personnalisés sans les inclure dans le type.
std::make_shared
N'est pas simplement un raccourci pour std::shared_ptr<Type> ptr(new Type(...));
. Il fait quelque chose que vous ne pouvez pas vous en passer.
Pour faire son travail, std::shared_ptr
Doit allouer un bloc de suivi en plus de conserver la mémoire pour le pointeur actuel. Cependant, comme std::make_shared
Alloue l'objet réel, il est possible que std::make_shared
Alloue à la fois l'objet et le bloc de suivi dans le même bloc de mémoire.
Ainsi, alors que std::shared_ptr<Type> ptr = new Type(...);
serait deux affectations de mémoire (une pour le new
, une dans le bloc de suivi std::shared_ptr
), std::make_shared<Type>(...)
allouerait n bloc de mémoire.
C'est important pour de nombreux utilisateurs potentiels de std::shared_ptr
. La seule chose que ferait un std::make_unique
Est un peu plus pratique. Rien de plus que ça.
En C++ 11 ...
est également utilisé (dans le code du 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 expansé et qu’elle appliquera simplement l’expression à chacun des éléments du pack.
Par exemple, en vous basant sur votre exemple:
std::forward<Args>(args)... -> std::forward<int>(1), std::forward<int>(2),
std::forward<int>(3)
std::forward<Args...>(args...) -> std::forward<int, int, int>(1,2,3)
Ce dernier étant incorrect je pense.
En outre, le paquet d'arguments ne peut pas être passé à une fonction non développée. Je ne suis pas sûr d'un paquet de paramètres de modèle.
Inspiré par l’implémentation de Stephan T. Lavavej, j’ai pensé qu’il pourrait être agréable d’avoir un make_unique qui prenne en charge les étendues de tableaux, c’est sur github et j'aimerais recevoir des commentaires à ce sujet. Cela vous permet de faire ceci:
// create unique_ptr to an array of 100 integers
auto a = make_unique<int[100]>();
// create a unique_ptr to an array of 100 integers and
// set the first three elements to 1,2,3
auto b = make_unique<int[100]>(1,2,3);