J'ai ce code qui ne fonctionne pas, mais je pense que l'intention est claire:
testmakeshared.cpp
#include <memory>
class A {
public:
static ::std::shared_ptr<A> create() {
return ::std::make_shared<A>();
}
protected:
A() {}
A(const A &) = delete;
const A &operator =(const A &) = delete;
};
::std::shared_ptr<A> foo()
{
return A::create();
}
Mais j'obtiens cette erreur lorsque je le compile:
g++ -std=c++0x -march=native -mtune=native -O3 -Wall testmakeshared.cpp
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:52:0,
from /usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/memory:86,
from testmakeshared.cpp:1:
testmakeshared.cpp: In constructor ‘std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc) [with _Tp = A, _Alloc = std::allocator<A>, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’:
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:518:8: instantiated from ‘std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:986:35: instantiated from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A, __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2u]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:313:64: instantiated from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>, _Args = {}, _Tp = A]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:531:39: instantiated from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = A, _Alloc = std::allocator<A>, _Args = {}]’
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr.h:547:42: instantiated from ‘std::shared_ptr<_Tp1> std::make_shared(_Args&& ...) [with _Tp = A, _Args = {}]’
testmakeshared.cpp:6:40: instantiated from here
testmakeshared.cpp:10:8: error: ‘A::A()’ is protected
/usr/lib/gcc/x86_64-redhat-linux/4.6.1/../../../../include/c++/4.6.1/bits/shared_ptr_base.h:400:2: error: within this context
Compilation exited abnormally with code 1 at Tue Nov 15 07:32:58
Ce message dit essentiellement qu'une méthode aléatoire dans la pile d'instanciation de modèle de ::std::make_shared
ne peut pas accéder au constructeur car il est protégé.
Mais je veux vraiment utiliser les deux ::std::make_shared
et empêcher quiconque de créer un objet de cette classe qui n'est pas pointé par un ::std::shared_ptr
. Y a-t-il un moyen d'accomplir cela?
c++
c++11
shared-ptr
Très varié
la source
la source
Réponses:
Cette réponse est probablement meilleure, et celle que j'accepterai probablement. Mais j'ai également proposé une méthode plus moche, mais qui laisse tout de même être en ligne et ne nécessite pas de classe dérivée:
Edit 2017-01-06: J'ai changé cela pour indiquer clairement que cette idée est clairement et simplement extensible aux constructeurs qui prennent des arguments parce que d'autres personnes fournissaient des réponses dans ce sens et semblaient confuses à ce sujet.
la source
protected
au lieu deprivate
. Et par «ça», je fais référence à lathis_is_private
classe, qui devrait peut-être être renommée dans ce cas. Je l'appelle généralementconstructor_access
dans mon code.{}
pour la balise privée sans avoir accès au nom du type (testé avec g ++ 4.9.0). Sans paramètres réels, il essaie de construire àA
partir de {}, bien que je ne sache pas pourquoi, et échoue. Je pense que rendre le constructeur this_is_private privé et fournir une méthode statique pour le créer le corrige, car il ne devrait y avoir aucun moyen d'accéder à cette méthode de l'extérieur à moins que vous ne fuyiez le type dans une signature de fonction membre.this_is_private
un ctor privé, vous pouvez faire de la classe A un ami. Semble fermer la brèche.En regardant les exigences de la
std::make_shared
création de shared_ptr 20.7.2.2.6 [util.smartptr.shared.create], paragraphe 1:Étant donné que l'exigence est spécifiée de manière inconditionnelle en fonction de cette expression et que des choses comme la portée ne sont pas prises en compte, je pense que des astuces comme l'amitié sont tout à fait possibles.
Une solution simple consiste à dériver de
A
. Cela n'a pas besoin de créerA
une interface ou même un type polymorphe.la source
shared_ptr
stocke un deleter au moment de l'instanciation, et si vous utilisezmake_shared
le deleter doit absolument utiliser le bon type.Peut-être la solution la plus simple. Basé sur la réponse précédente de Mohit Aron et incorporant la suggestion de dlf.
la source
A
a des constructeurs par défaut , vous devrez également les exposer:struct make_shared_enabler : public A { template <typename... Args> make_shared_enabler(Args &&... args):A(std::forward<Args>(args)...) {} };
. Cela rend tous les constructeurs privésA
visibles en tant quemake_shared_enabler
constructeurs. L'utilisation de la fonction d'héritage des constructeurs (using A::A;
) ne semble pas aider ici car les constructeurs seront toujours privés.class A { ... private: struct A_shared_enabler; }; class A::A_shared_enabler : public A { ... }
. Voir ici cpp.sh/65qbr .Voici une solution intéressante pour cela:
la source
MakeSharedEnabler
localement à l'intérieurA::Create()
.Que dis-tu de ça?
la source
::std::make_shared
a des fonctionnalités au-delà de la simple création d'un shared_ptr à quelque chose. Il alloue le nombre de références avec l'objet afin qu'ils soient situés à proximité les uns des autres. Je veux vraiment, vraiment utiliser::std::make_shared
.la source
Comme je n'aimais pas les réponses déjà fournies, j'ai décidé de rechercher et j'ai trouvé une solution qui n'est pas aussi générique que les réponses précédentes mais je l'aime mieux (tm). Rétrospectivement, ce n'est pas beaucoup plus agréable que celui fourni par Omnifarius mais il pourrait y avoir d'autres personnes qui l'aiment aussi :)
Ce n'est pas moi qui l'ai inventé, mais c'est l'idée de Jonathan Wakely (développeur GCC).
Malheureusement, cela ne fonctionne pas avec tous les compilateurs car il repose sur un petit changement dans l'implémentation de std :: allocate_shared. Mais ce changement est maintenant une mise à jour proposée pour les bibliothèques standard, il pourrait donc être pris en charge par tous les compilateurs à l'avenir. Cela fonctionne sur GCC 4.7.
La demande de modification du groupe de travail sur la bibliothèque standard C ++ est ici: http://lwg.github.com/issues/lwg-active.html#2070
Le patch GCC avec un exemple d'utilisation est ici: http://old.nabble.com/Re%3A--v3--Implement-pointer_traits-and-allocator_traits-p31723738.html
La solution fonctionne sur l'idée d'utiliser std :: allocate_shared (au lieu de std :: make_shared) avec un allocateur personnalisé qui est déclaré ami à la classe avec le constructeur privé.
L'exemple de l'OP ressemblerait à ceci:
Un exemple plus complexe basé sur l'utilitaire sur lequel je travaille. Avec cela, je ne pouvais pas utiliser la solution de Luc. Mais celui d'Omnifarius pourrait être adapté. Ce n'est pas que, bien que dans l'exemple précédent, tout le monde puisse créer un objet A en utilisant MyAlloc dans celui-ci, il n'y a pas moyen de créer A ou B en plus de la méthode create ().
la source
Idéalement, je pense que la solution parfaite nécessiterait des ajouts à la norme C ++. Andrew Schepler propose ce qui suit:
(Allez ici pour tout le fil)
Usage
Si / quand ce qui précède est ajouté à la norme, nous ferions simplement:
Si cela vous semble également un ajout important à la norme, n'hésitez pas à ajouter vos 2 cents au groupe Google isocpp lié.
la source
Je me rends compte que ce fil est assez ancien, mais j'ai trouvé une réponse qui ne nécessite pas d'héritage ou d'arguments supplémentaires au constructeur que je ne pourrais pas voir ailleurs. Ce n'est cependant pas portable:
J'ai testé sur Windows et Linux, il peut avoir besoin d'être peaufiné pour différentes plates-formes.
la source
std::shared_ptr_access
à la norme, qui pourrait être considérée comme permettant de faire ce qui précède de manière simple et portable.Il y a un problème plus poilu et intéressant qui se produit lorsque vous avez deux classes A et B strictement liées qui fonctionnent ensemble.
Disons que A est la "classe maître" et B son "esclave". Si vous souhaitez restreindre l'instanciation de B uniquement à A, vous rendez le constructeur de B privé et l'ami B vers A comme ceci
Malheureusement, appeler à
std::make_shared<B>()
partir d'une méthode deA
fera se plaindre du compilateur d'B::B()
être privé.Ma solution à cela est de créer une
Pass
classe factice publique (tout commenullptr_t
) à l'intérieurB
qui a un constructeur privé et qui est ami avecA
et rendB
le constructeur de public public et ajouterPass
à ses arguments, comme ceci.la source
Si vous souhaitez également activer un constuctor qui prend des arguments, cela peut aider un peu.
la source
[Edit] J'ai lu le fil noté ci-dessus sur une
std::shared_ptr_access<>
proposition standardisée . À l'intérieur, il y avait une réponse indiquant un correctifstd::allocate_shared<>
et un exemple de son utilisation. Je l'ai adapté à un modèle d'usine ci-dessous, et l'ai testé sous gcc C ++ 11/14/17. Cela fonctionne également avecstd::enable_shared_from_this<>
, donc serait évidemment préférable à ma solution originale dans cette réponse. C'est ici...[Orig] J'ai trouvé une solution en utilisant le constructeur d'alias de pointeur partagé. Il permet à la fois au ctor et au dtor d'être privés, ainsi que l'utilisation du spécificateur final.
Notez que l'approche ci-dessus ne fonctionne pas bien
std::enable_shared_from_this<>
car l'initialestd::shared_ptr<>
est au wrapper et non au type lui-même. Nous pouvons y remédier avec une classe équivalente compatible avec l'usine ...Enfin, quelqu'un a dit que clang se plaignait du fait que Factory :: Type était privé lorsqu'il était utilisé comme ami, alors rendez-le public si c'est le cas. L'exposer ne fait aucun mal.
la source
J'ai eu le même problème, mais aucune des réponses existantes n'était vraiment satisfaisante car j'ai besoin de transmettre des arguments au constructeur protégé. De plus, je dois le faire pour plusieurs classes, chacune prenant des arguments différents.
À cet effet, et en me basant sur plusieurs des réponses existantes qui utilisent toutes des méthodes similaires, je présente cette petite pépite:
la source
La racine du problème est que si la fonction ou la classe que vous utilisez fait des appels de niveau inférieur à votre constructeur, elle doit également être amicale. std :: make_shared n'est pas la fonction qui appelle réellement votre constructeur, donc cela ne fait aucune différence.
std :: _ Ref_count_obj appelle en fait votre constructeur, il doit donc être un ami. Comme c'est un peu obscur, j'utilise une macro
Ensuite, votre déclaration de classe semble assez simple. Vous pouvez créer une seule macro pour déclarer le ptr et la classe si vous préférez.
C'est en fait une question importante. Pour rendre le code portable maintenable, vous devez masquer autant que possible l'implémentation.
cache un peu la façon dont vous gérez votre pointeur intelligent, vous devez être sûr d'utiliser votre typedef. Mais si vous devez toujours en créer un avec make_shared, cela va à l'encontre de l'objectif.
L'exemple ci-dessus force le code utilisant votre classe à utiliser votre constructeur de pointeur intelligent, ce qui signifie que si vous passez à une nouvelle saveur de pointeur intelligent, vous modifiez votre déclaration de classe et vous avez de bonnes chances d'être terminé. NE supposez PAS que votre prochain boss ou projet utilisera stl, boost, etc. pour le changer un jour.
En faisant cela pendant près de 30 ans, j'ai payé un gros prix en temps, en douleur et en effets secondaires pour réparer cela alors que cela était mal fait il y a des années.
la source
std::_Ref_count_obj
est un détail de mise en œuvre. Cela signifie que cette solution pourrait fonctionner pour vous, pour l'instant, sur votre plate-forme. Mais cela peut ne pas fonctionner pour les autres et peut cesser de fonctionner à chaque fois que votre compilateur est mis à jour ou peut-être même si vous changez simplement les indicateurs de compilation.Vous pouvez utiliser ceci:
la source
std::make_shared
.la source