Pour une classe Foo, y a-t-il un moyen d'interdire sa construction sans lui donner un nom?
Par exemple:
Foo("hi");
Et ne l'autorisez que si vous lui donnez un nom, comme le suivant?
Foo my_foo("hi");
La durée de vie du premier n'est que la déclaration, et le second est le bloc englobant. Dans mon cas d'utilisation, Foo
mesure le temps entre le constructeur et le destructeur. Comme je ne fais jamais référence à la variable locale, j'oublie souvent de la mettre et je change accidentellement la durée de vie. Je voudrais plutôt obtenir une erreur de compilation.
return std::string("Foo");
)Réponses:
Une autre solution macro-basée:
La déclaration
Foo("hi");
s'étend àclass Foo("hi");
, qui est mal formée; mais seFoo a("hi")
développeclass Foo a("hi")
, ce qui est correct.Cela présente l'avantage d'être à la fois compatible avec le code source et le code binaire existant (correct). (Cette affirmation n'est pas tout à fait correcte - veuillez consulter le commentaire de Johannes Schaub et la discussion qui s'ensuit ci-dessous: "Comment pouvez-vous savoir que la source est compatible avec le code existant? Son ami inclut son en-tête et a void f () {int Foo = 0;} qui précédemment compilé correctement et maintenant mal compilé! De plus, chaque ligne qui définit une fonction membre de la classe Foo échoue: void class Foo :: bar () {} " )
la source
void f() { int Foo = 0; }
déjà compilé très bien et maintenant mal compilé! En outre, chaque ligne qui définit une fonction de membre de la classe Foo échoue:void class Foo::bar() {}
.Foo("Hi")
à- dire à l' intérieur de Foo.cpp maintenantclass Foo("hi");
c'était OK pour la compilation.Que diriez-vous d'un petit hack
la source
Foo a("hi");
(sansclass
) serait également une erreur.Foo
car elle a priorité sur une classe.Foo::Foo("hi")
n'est pas autorisé en C ++.Rendez le constructeur privé mais donnez à la classe une méthode de création .
la source
Foo::create();
surFoo const & x = Foo::create();
std::common_type<Foo>::type()
et vous obtenez un temporaire. Ou mêmetypedef Foo bar; bar()
.std::common_type<Foo>::type()
par erreur. Laisser de côté leFoo const & x = ...
par accident est totalement crédible.Celui-ci n'entraîne pas une erreur du compilateur, mais une erreur d'exécution. Au lieu de mesurer un mauvais moment, vous obtenez une exception qui peut également être acceptable.
Tout constructeur que vous souhaitez protéger a besoin d'un argument par défaut sur lequel
set(guard)
est appelé.Les caractéristiques sont:
Le cas de
f2
,f3
et le retour de"hello"
ne pas être voulu. Pour éviter le rejet, vous pouvez autoriser la source d'une copie à être temporaire, en réinitialisant leguard
pour nous garder maintenant au lieu de la source de la copie. Maintenant, vous voyez également pourquoi nous avons utilisé les pointeurs ci-dessus - cela nous permet d'être flexibles.Les caractéristiques pour
f2
,f3
etreturn "hello"
sont maintenant toujours// OK
.la source
Foo f = "hello"; // may throw
Cela suffit pour me faire peur de ne jamais utiliser ce code.explicit
, puis ce code ne se compile plus. le but était de fotbid le temporaire, et c'est le cas. si vous avez peur, vous pouvez faire en sorte qu'il ne soit pas lancé en définissant la source d'une copie dans le constructeur de copie ou de déplacement sur une valeur non-temporaire. alors seul l'objet final de plusieurs copies peut être jeté s'il finit toujours par devenir temporaire.Foo
objet est également temporaire et que sa durée de vie se termine par la même expression que l'argument par défaut, alors leFoo
dtor de l' objet sera appelé avant le dtor de l'argument par défaut, car le premier a été créé après le second.Foo(...);
etFoo foo(...);
de l'intérieur duFoo
.Il y a quelques années, j'ai écrit un correctif pour le compilateur GNU C ++ qui ajoute une nouvelle option d'avertissement pour cette situation. Ceci est suivi dans un élément Bugzilla .
Malheureusement, GCC Bugzilla est un cimetière où les suggestions de fonctionnalités bien pensées incluses dans les correctifs vont mourir. :)
Cela a été motivé par le désir d'attraper exactement le genre de bogues qui font l'objet de cette question dans un code qui utilise des objets locaux comme gadgets pour verrouiller et déverrouiller, mesurer le temps d'exécution, etc.
la source
En l'état, avec votre implémentation, vous ne pouvez pas faire cela, mais vous pouvez utiliser cette règle à votre avantage:
Vous pouvez déplacer le code de la classe vers une fonction autonome qui prend un paramètre de référence non const. Si vous le faites, vous obtiendrez une erreur du compilateur si un temporaire tente de se lier à la référence non const.
Exemple de code
Production
la source
x
est un objet nommé, il n'est donc pas clair si nous voulons vraiment l'interdire. Si le constructeur que vous auriez utilisé est explicite, les gens le feraient instinctivementFoo f = Foo("hello");
. Je pense qu'ils se fâcheraient si cela échouait. Ma solution l'a initialement rejetée (et des cas très similaires) avec une exception / affirmation-échec et quelqu'un s'est plaint.Vous n'avez tout simplement pas de constructeur par défaut et vous avez besoin d'une référence à une instance dans chaque constructeur.
la source
S(selfRef, a);
. : /S(SelfRef, S const& s) { assert(&s == this); }
, si une erreur d'exécution est acceptable.Non, j'ai bien peur que ce ne soit pas possible. Mais vous pouvez obtenir le même effet en créant une macro.
Avec cela en place, vous pouvez simplement écrire FOO (x) au lieu de Foo my_foo (x).
la source
Foo();
.class Do_not_use_this_class_directly_Only_use_it_via_the_FOO_macro;
Puisque l'objectif principal est d'éviter les bogues, considérez ceci:
De cette façon, vous ne pouvez pas oublier de nommer la variable et vous ne pouvez pas oublier d'écrire
struct
. Verbose, mais sûr.la source
Déclarez un constructeur paramétrique comme explicite et personne ne créera jamais un objet de cette classe sans le vouloir.
Par exemple
ne peut être utilisé que de cette façon
mais jamais de cette façon (via un temporaire sur la pile)
Voir aussi: Alexandrescu, C ++ Coding Standards, Item 40.
la source
fun(Foo("text"));
.