C ++ n'a pas l'équivalent du mot - self
clé PHP , qui évalue le type de la classe englobante.
Il est assez facile de le simuler par classe:
struct Foo
{
typedef Foo self;
};
mais je devais écrire Foo
nouveau. Peut-être que je vais me tromper un jour et provoquer un bug silencieux.
Puis-je utiliser une combinaison de decltype
et d'amis pour faire ce travail "de manière autonome"? J'ai déjà essayé ce qui suit mais ce this
n'est pas valide à cet endroit:
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
(Je ne vais pas m'inquiéter de l'équivalent de static
, qui fait la même chose mais avec une liaison tardive.)
this_t
serait probablement plus aligné avec la dénomination C ++ classique.auto()
et~auto()
pour les ctors / dtors. Intéressant pour dire le moins. Si utilisé à cette fin, peut-êtretypedef auto self;
, mais cela me semble un peu sommaire.decltype(class)
, peut-être avec undecltype(struct)
équivalent. C'est beaucoup plus clair queauto
dans un contexte spécifique et je ne vois aucun problème avec son intégration dans le langage basé surdecltype(auto)
.void _check() { static_assert(std::is_same<self&, decltype(*this)>::value, "Correct your self type"); }
Ne fonctionne pas avec les modèles de classe, bien que ...Réponses:
Voici comment vous pouvez le faire sans répéter le type de Foo:
Si vous souhaitez dériver de,
Foo
vous devez utiliser la macroWITH_SELF_DERIVED
de la manière suivante:Vous pouvez même faire plusieurs héritages avec autant de classes de base que vous le souhaitez (grâce aux modèles variadiques et aux macros variadiques):
J'ai vérifié que cela fonctionne sur gcc 4.8 et clang 3.4.
la source
auto
etdecltype
ou dans ce cas deself
.template<typename T>class Self{protected: typedef T self;}; class WITH_SELF(Foo) : public Bar, private Baz {};
aurait été plus simple et permettrait un contrôle plus précis de l'héritage - des raisons contre?Une solution de contournement possible (car vous devez toujours écrire le type une fois):
Pour une version plus sûre, nous pourrions nous assurer que
T
dérive réellement deSelf<T>
:Notez qu'une
static_assert
fonction à l'intérieur d'une fonction membre est probablement le seul moyen de vérifier, car les types passésstd::is_base_of
doivent être complets.la source
typename
dans le typedef. Et comme cela ne réduit pas le nombre de licenciements, je ne pense pas que ce soit une alternative viable.Foo
nom.Foo
, vous devez soit: (1) propager le T vers le haut au descendant de la feuille, ou (2) vous souvenir d'hériter de SelfT plusieurs fois , ou (3) accepter que tous les enfants soient la base .. utilisable, mais peu jolie.self
plutôt questatic
, ce n'est pas un problème.Vous pouvez utiliser une macro au lieu d'une déclaration de classe ordinaire, qui le fera pour vous.
Et puis utilisez comme
#define END_CLASS };
aiderait probablement la lisibilité.Vous pouvez également prendre @ Paranaix's
Self
et l'utiliser (cela commence à devenir vraiment hackish)la source
{
, donc}
c'est "suspendu", ce que les éditeurs de texte n'aimeraient probablement pas aussi.CLASS_WITH_SELF(foo) { … };
- et je pense que c'est impossible à réaliser.Self
.Je n'ai aucune preuve positive mais je pense que c'est impossible. Ce qui suit échoue - pour la même raison que votre tentative - et je pense que c'est le plus loin possible:
Essentiellement, cela démontre que la portée à laquelle nous voulons déclarer notre typedef n'a tout simplement aucun accès (direct ou indirect) à
this
, et il n'y a pas d'autre moyen (indépendant du compilateur) d'accéder au type ou au nom de la classe.la source
decltype
sont un contexte non évalué, donc invoquer la fonction membre n'est pas le problème (cela ne sera pas tenté)struct S { int i; typedef decltype(i) Int; };
fonctionne même s'ili
s'agit d'un membre de données non statique. Cela fonctionne car il ydecltype
a une exception spéciale où un nom simple n'est pas évalué comme une expression. Mais je ne vois aucun moyen d'utiliser cette possibilité d'une manière qui réponde à la question.Ce qui fonctionne à la fois dans GCC et clang est de créer un typedef qui se réfère à
this
en utilisantthis
dans le type de retour de fin d'une fonction typedef. Comme il ne s'agit pas de la déclaration d'une fonction membre statique, l'utilisation dethis
est tolérée. Vous pouvez ensuite utiliser ce typedef pour définirself
.Malheureusement, une lecture stricte de la norme indique que même cela n'est pas valable. Ce que fait clang est de vérifier que ce
this
n'est pas utilisé dans la définition d'une fonction membre statique. Et ici, ce n'est en effet pas. GCC ne se soucie pas de savoir s'ilthis
est utilisé dans un type de retour de fin quel que soit le type de fonction, il le permet même pourstatic
les fonctions membres. Cependant, ce que la norme exige réellement, c'est quethis
n'est pas utilisée en dehors de la définition d'une fonction membre non statique (ou d'un initialiseur de membre de données non statique). Intel a raison et rejette cela.Étant donné que:
this
n'est autorisé que dans les initialiseurs de membres de données non statiques et les fonctions membres non statiques ([expr.prim.general] p5),this
peuvent être utilisées ([over.call.func] p3),Je pense que je peux dire de manière concluante qu'il n'y a aucun moyen de mettre en œuvre
self
sans inclure d'une manière ou d'une autre, quelque part, le nom du type.Edit : Il y a un défaut dans mon raisonnement antérieur. "Les fonctions membres non statiques ne peuvent être appelées que par un nom non qualifié, même dans des contextes non évalués, lorsque cela peut être utilisé ([over.call.func] p3)", est incorrect. Qu'est-ce que c'est réellement dit en est
À l'intérieur d'une fonction membre statique,
this
peut ne pas apparaître, mais elle existe toujours.Cependant, d'après les commentaires, à l'intérieur d'une fonction membre statique, la transformation de
f()
en(*this).f()
ne serait pas effectuée, et si elle n'est pas effectuée, alors [expr.call] p1 est violé:car il n'y aurait pas d'accès aux membres. Donc même ça ne marcherait pas.
la source
_self_fn_1()
c'est "transformé" en(*this)._self_fn_1()
. Je ne sais pas si cela le rend illégal, cependant.X
dans un contexte oùthis
peut être utilisé", donc je ne pense pas que la transformation soit effectuée.auto _self_fn_1() -> decltype(*this); auto _self_fn_1() const -> decltype(*this);
?)const
surcharges, cependant: ce n'est pas un problème. 5.1p3 a été spécifiquement modifié pour s'appliquer également aux fonctions membres statiques, et dit que le type dethis
estFoo*
/Bar*
(sansconst
), car il n'y a pasconst
dans la déclaration de_self_fn_2
.cela ne fonctionne pas sur les types de modèle, comme
self_check
on ne l'appelle pas, donc lestatic_assert
n'est pas évalué.Nous pouvons également faire des hacks pour que cela fonctionne pour
template
s, mais cela a un coût d'exécution mineur.un
struct
octet vide de taille 1 est créé dans votre classe. Si votre type est instancié,self
est testé.la source
template
des options de support de classe.inline
. Cela signifie que vous n'avez pas du tout besoin d'écrireinline
. Donc, si vous avez écritinline
devant chacune de ces définitions de fonction membre de classe pour toute votre carrière, vous pouvez vous arrêter maintenant;)Je pense aussi que c'est impossible, voici une autre tentative intéressante mais échouée à
this
mon humble avis qui évite l' accès:qui échoue car C ++ vous oblige à vous qualifier
self_f
avec la classe lorsque vous voulez prendre son adresse :(la source
int T::*
pointeur régulier vers une variable membre. Etint self_var; typedef decltype(&self_var) self_ptr
ça ne marche pas non plus, c'est juste un habituéint*
.J'ai récemment découvert que cela
*this
est autorisé dans un initialiseur d'accolade ou d'égalité . Décrit au § 5.1.1 (à partir du projet de travail n3337 ):Dans cet esprit, le code suivant:
dépasse Daniel Frey
static_assert
.Live example
la source
test
si= this
, non? Et pourquoi pas justeusing self = Foo*;
test
être de type, euh,Foo *
!À moins que le type ne doive être le type de membre de la classe englobante, vous pouvez remplacer l'utilisation de
self
pardecltype(*this)
. Si vous l'utilisez à de nombreux endroits dans votre code, vous pouvez définir une macroSELF
comme suit:la source
self
faire référence à la classe immédiatement englobante et non à la classe externe? Mais je ne connais pas bien php.Fournissez ma version. La meilleure chose est que son utilisation est la même que la classe native. Cependant, cela ne fonctionne pas pour les classes de modèles.
la source
En me basant sur la réponse de hvd, j'ai trouvé que la seule chose qui manquait était la suppression de la référence, c'est pourquoi la vérification std :: is_same échoue (b / c le type résultant est en fait une référence au type). Maintenant, cette macro sans paramètre peut faire tout le travail. Exemple de travail ci-dessous (j'utilise GCC 8.1.1).
la source
Je vais répéter la solution évidente de "devoir le faire vous-même". Il s'agit de la version succincte du code C ++ 11, qui fonctionne à la fois avec des classes simples et des modèles de classes:
Vous pouvez le voir en action chez ideone . La genèse menant à ce résultat est ci-dessous:
Cela pose le problème évident de copier-coller le code dans une classe différente et d'oublier de changer XYZ, comme ici:
Ma première approche n'était pas très originale - créer une fonction, comme celle-ci:
C'est un peu long, mais veuillez rester avec moi ici. Cela présente l'avantage de travailler en C ++ 03 sans
decltype
, car la__self_check_helper
fonction est utilisée pour déduire le type dethis
. En outre, il n'y a passtatic_assert
, mais l'sizeof()
astuce est utilisée à la place. Vous pouvez le rendre beaucoup plus court pour C ++ 0x. Maintenant, cela ne fonctionnera pas pour les modèles. De plus, il y a un problème mineur avec la macro qui n'attend pas de point-virgule à la fin, si elle compile avec pedantic, elle se plaindra d'un point-virgule supplémentaire inutile (ou vous vous retrouverez avec une macro étrange ne se terminant pas par un point-virgule dans le corps deXYZ
etABC
).Faire une vérification sur le
Type
qui est passéDECLARE_SELF
n'est pas une option, car cela ne vérifierait que laXYZ
classe (ce qui est ok), inconscient deABC
(qui a une erreur). Et ensuite ça m'a frappé. Une solution zéro coût de stockage sans stockage supplémentaire qui fonctionne avec des modèles:Cela rend simplement une assertion statique sur une valeur d'énumération unique (ou au moins unique dans le cas où vous n'écrivez pas tout votre code sur une seule ligne), aucune supercherie de comparaison de type n'est utilisée, et cela fonctionne comme une assertion statique, même dans les modèles . Et en prime - le point-virgule final est maintenant requis :).
Je tiens à remercier Yakk de m'avoir donné une bonne inspiration. Je n'écrirais pas cela sans avoir d'abord vu sa réponse.
Testé avec VS 2008 et g ++ 4.6.3. En effet, avec l' exemple
XYZ
etABC
, il se plaint:Maintenant, si nous faisons d'ABC un modèle:
Nous allons obtenir:
Seule la vérification du numéro de ligne s'est déclenchée, car la vérification de la fonction n'a pas été compilée (comme prévu).
Avec C ++ 0x (et sans les mauvais traits de soulignement), vous auriez juste besoin de:
Je crois que le bit CStaticAssert est malheureusement toujours nécessaire car il produit un type, qui est typé dans le corps du modèle (je suppose que la même chose ne peut pas être faite avec
static_assert
). L'avantage de cette approche est toujours son coût nul.la source
static_assert
ici, n'est-ce pas? De plus, votre code complet est invalide car vous utilisez des identifiants illégaux (réservés).Je ne sais pas tout sur ces modèles farfelus, que diriez-vous de quelque chose de super simple:
Travail terminé, sauf si vous ne supportez pas quelques macros. Vous pouvez même utiliser
CLASSNAME
pour déclarer votre (vos) constructeur (s) (et, bien sûr, destructeur).Démo en direct .
la source