la création d'un shared_ptr de unique_ptr
Dans un morceau de code que j'ai revu dernièrement, qui a compilé fine avec g++-4.6
, j'ai rencontré un étrange essayez de créer un std::shared_ptr
de std::unique_ptr
:
std::unique_ptr<Foo> foo...
std::make_shared<Foo>(std::move(foo));
Cela semble plutôt étrange pour moi. Cela devrait être std::shared_ptr<Foo>(std::move(foo));
autant que je sache, même si je ne suis pas tout à fait familier avec les se déplace (et je sais de std::move
est seulement un casting, rien ne se est déplacé).
De vérifier avec différents compilateurs sur cette SSC(NUC*)E
#include <memory>
int main()
{
std::unique_ptr<int> foo(new int);
std::make_shared<int>(std::move(foo));
}
Résultats de la compilation:
- g++-4.4.7 donne erreur de compilation
- g++-4.6.4 compile sans erreur
- g++-4.7.3 donne erreur interne du compilateur
- g++-4.8.1 donne erreur de compilation
- clang++-3.2.1 compile sans erreur
La question est donc: le compilateur est correct du point de vue de la norme? La norme besoin de cela pour être une instruction non valide, une instruction valide ou est-ce tout simplement pas défini?
Plus
Nous avons convenu que certains de ces compilateurs, comme clang++ et g++-4.6.4, de permettre la conversion, alors qu'ils ne le sont pas. Cependant, avec g++-4.7.3 (qui produit une erreur interne du compilateur sur std::make_shared<Foo>(std::move(foo));
), correctement rejette int bar(std::move(foo));
En raison de cette énorme différence dans le comportement, je pars de la question, comme il est, même si une partie de celui-ci serait responsable de la réduction de int bar(std::move(foo));
.
*) NUC: Pas universellement compilable
- Sauf
Foo
a un constructeur qui prend unstd::unique_ptr<Foo>
il ne devrait pas compiler. - c'est source de confusion en disant: "compile avec de corriger l'erreur" quand vous voulez dire "ne pas compiler". Et la question du titre est trompeur, vous n'êtes pas créer un shared_ptr à partir d'un unique_ptr, vous êtes en train de créer un
int
à partir d'un unique_ptr. - J'ai modifié la formulation. Cependant je vais laisser le titre tel qu'il est. Pourquoi? Le but du réalisateur était de créer un
std::shared_ptr<Foo>
de cette façon. Ce n'est pas le point de cette question de créer unint
à partir d'ununique_ptr
, mais pourquoi les différents compilateurs permettent à cette déclaration ou à échouer lamentablement. - juste pour la référence, j'ai vérifié si
unique_ptr
est implicitement convertible en un pointeur normal (mais il n'est pas) - et il ne devrait pas l'être. Ce serait briser le concept de
*unique*_ptr
- Pourquoi ne pas tout simplement utiliser la sortie:
std::unique_ptr<int> foo(new int); std::shared_ptr<int> shptr(foo.release());
? (Il n'est pas de compilateur bug.) - Oui, c'était ma première lecture de la question. Le point est, la ligne
std::make_shared<int>(std::move(foo));
ne devrait pas compiler. C'est la vraie question de savoir si la norme le permet. À mon avis, c'est un bug du compilateur. - Comme je l'ai dit, ce n'était pas mon code et que l'intention était de faire un pointeur partagé d'être unique. La manière correcte est via le shared_ptr ctor qui prend un unique_ptr. La mauvaise manière, montré ici produit une variété de différents comportements dans les compilateurs, dont certains sont mauvais.
- Ne pas écrire
std::shared_ptr<int> shptr(foo.release());
! C'est du code dangereux qui peut causer une fuite de mémoire. Sishared_ptr
ne parvient pas à allouer de son bloc de référence et déclenche une exception vous avez maintenant une fuite de mémoire. - ce ne sera pas une fuite de mémoire. la référence du rpc, dit que, dans le cas de l'exception bad_alloc delete ptr sera appelé
Vous devez vous connecter pour publier un commentaire.
Mise à JOUR 2: Ce bug a été corrigé dans Clang dans r191150. GCC rejette le code avec un message d'erreur approprié.
Mise à JOUR: j'ai présenté une rapport de bug. Le code suivant sur ma machine avec clang++ 3.4 (tronc 191037)
imprime ce:
Comme vous pouvez le voir, le
unique_ptr
n'a pas été déplacé de. La norme garanties qu'il devrait être à null après qu'il a été déplacé à partir. Leshared_ptr
points pour une valeur incorrecte.La chose étrange est qu'il compile sans avertissement et valgrind ne pas signaler tous les problèmes, pas de fuite, pas de corruption de segment. Bizarre.
Le bon comportement est montré si je créer
s_ptr
avec leshared_ptr
ctor prendre une rvalue ref à ununique_ptr
au lieu demake_shared
:Il imprime:
Comme vous le voyez,
u_ptr
est nulle après le déménagement tel que requis par la norme ets_ptr
points à la valeur correcte. C'est le comportement correct.(L'original de la réponse.)
En tant que Simple a souligné: "À moins que Toto a un constructeur qui prend un std::unique_ptr, il ne devrait pas compiler."
Pour développer un peu:
make_shared
transmet ses arguments pour T de constructeur. Si T n'ont pas ctor qui pourrait accepter queunique_ptr<T>&&
c'est une erreur de compilation.Cependant, il est facile de corriger ce code et obtenir ce que vous voulez (démo en ligne):
Le point est:
make_shared
est la mauvaise chose à utiliser dans cette situation.shared_ptr
a un ctor qui accepte ununique_ptr<Y,Deleter>&&
, voir (13) à l'ctors deshared_ptr
.g++-4.6.4
permet l'utilisation intermédiaire deoperator bool()
convertir d'abord deunique_ptr<T>
àbool
àint
. Probablement, clang++ en fait de même.std::make_shared<int>(std::move(foo));
ne devrait pas compiler. Si elle le fait, c'est un bug. Par la voie, où avez-vous trouver cette info?operator bool()
, compilé et exécuté. Le débogage de déclaration apparaît, par conséquent, il a été utilisé. C'est aussi simple que cela. 😉Cela ne devrait pas compiler. Si l'on fait abstraction de l'unicité et de la sharedness des pointeurs pour un moment, il est en fait, d'essayer de le faire:
Cela signifie que c'est dynamiquement la création d'un
int
et l'initialisation d'une référence rvalue àstd::unique_ptr<int>
. Pourint
s, qui ne devrait pas compiler.Pour une classe générale
Foo
, il dépend de la classe. Si il a un constructeur prenant unstd::unique_ptr<Foo>
en valeur, const ref ou rvalue ref, il va travailler (mais peut-être pas ce que l'auteur a voulu). Dans d'autres cas, il ne devrait pas compiler.Voici un exemple qui réduit clang compile de façon incorrecte:
Clang utilise l'explicite bool opérateur de conversion pour initialiser le
int
(et le compilateur Intel ne trop.)Clang 3.4 ne permet pas:
mais il ne permet pas de:
Je pense que les deux doivent être rejetées. (Clang 3.3 et ICC permettent à la fois.)
J'ai ajouté cet exemple à la rapport de bug.