Pourquoi dois-je avoir pour modèle d'accès de la base de membres de la classe par le biais de la ce pointeur?
Si les classes ci-dessous n'ont pas été des modèles que je pourrait tout simplement x
dans le derived
classe. Cependant, avec le code ci-dessous, je ont pour utilisation this->x
. Pourquoi?
template <typename T>
class base {
protected:
int x;
};
template <typename T>
class derived : public base<T> {
public:
int f() { return this->x; }
};
int main() {
derived<int> d;
d.f();
return 0;
}
Ah mon dieu. Il a quelque chose à voir avec la recherche d'un nom. Si quelqu'un n'a pas répondu à ce sujet bientôt, je vais le chercher et de le poster (occupé maintenant).
Swangren: Désolé, j'ai manqué de il parmi l'offre de réponses lors de la publication de cette question. J'avais été à la recherche de la réponse pour un long temps avant que.
Cela arrive parce que les deux en phase de recherche de nom (pas tous les compilateurs utilisent par défaut) et dépendant de noms. Il y a 3 solutions à ce problème, d'autres que de préfixer le
Swangren: Désolé, j'ai manqué de il parmi l'offre de réponses lors de la publication de cette question. J'avais été à la recherche de la réponse pour un long temps avant que.
Cela arrive parce que les deux en phase de recherche de nom (pas tous les compilateurs utilisent par défaut) et dépendant de noms. Il y a 3 solutions à ce problème, d'autres que de préfixer le
x
avec this->
, à savoir : 1) Utilisez le préfixe base<T>::x
, 2) Ajouter un énoncé using base<T>::x
, 3) global commutateur de compilateur qui permet à la mode permissif. Les pros & les inconvénients de ces solutions sont décrites dans stackoverflow.com/questions/50321788/...OriginalL'auteur Ali | 2011-01-10
Vous devez vous connecter pour publier un commentaire.
Réponse courte: afin de rendre
x
une personne à charge nom, de sorte que la recherche est différée jusqu'à ce que le paramètre de modèle est connu.Réponse longue: quand un compilateur voit un modèle, il est censé effectuer certains contrôles immédiatement, sans voir le paramètre de modèle. D'autres sont différés jusqu'à ce que le paramètre est connu. Il est appelé à deux phases de compilation et de MSVC ne pas le faire, mais il est requis par la norme et mis en œuvre par les autres grands compilateurs. Si vous le souhaitez, le compilateur doit compiler le modèle dès qu'il la voit (une sorte de interne de l'arbre d'analyse de la représentation), et de reporter la compilation de l'instanciation jusqu'à ce que plus tard.
Les contrôles qui sont effectués sur le modèle lui-même, plutôt que sur les instanciations de, exiger que le compilateur être en mesure de résoudre la grammaire du code dans le modèle.
En C++ (et C), afin de résoudre la grammaire de code, vous avez parfois besoin de savoir si quelque chose est un type ou pas. Par exemple:
si A est un type qui déclare un pointeur (sans autre effet que de faire de l'ombre aux mondiaux
x
). Si A est un objet, c'est la multiplication (et à l'exception de quelques surcharge d'opérateur c'est illégal, l'affectation à une rvalue). Si c'est faux, cette erreur doit être diagnostiqué dans la phase 1, il est défini par la norme pour être une erreur dans le modèle, non pas dans une particulier de l'instanciation. Même si le modèle n'est jamais instanciée, si A est unint
puis le code ci-dessus est mal formé et doivent être diagnostiqués, comme il le serait sifoo
n'était pas un modèle, mais une simple fonction.Maintenant, la norme dit que les noms qui ne sont pas dépend des paramètres de modèle doit être résolu dans la phase 1.
A
ici n'est pas une personne à charge nom, il se réfère à la même chose quel que soit le typeT
. Il doit donc être défini avant que le modèle est défini pour être trouvé et vérifié dans la phase 1.T::A
serait un nom qui dépend de T. On ne peut pas savoir à l'étape 1 si c'est un type ou pas. Le type qui pourront éventuellement être utilisés commeT
dans une instanciation tout à fait probable n'est même pas encore défini, et même si c'était nous ne savons pas quel type(s) seront utilisées comme paramètre du modèle. Mais nous avons à résoudre la grammaire afin de faire de notre précieux phase 1 vérifie mal formé modèles. Si la norme est une règle pour dépendante des noms - le compilateur doit supposer qu'ils sont non-types, à moins qualifiés avectypename
de préciser qu'ils sont types, ou utilisé sans ambiguïté dans certains contextes. Par exemple, danstemplate <typename T> struct Foo : T::A {};
,T::A
est utilisée comme classe de base et est donc sans ambiguïté un type. SiFoo
est instancié par un type qui a un membre de donnéesA
au lieu d'une étude de type A, c'est une erreur dans le code fait l'instanciation (phase 2), et non pas une erreur dans le modèle (phase 1).Mais qu'un modèle de classe avec un dépendant de la classe de base?
Est un dépendant de nom ou pas? Avec les classes de base, tout nom pourrait apparaître dans la classe de base. Donc, on pourrait dire que l'Une est une personne à charge nom, et la traiter comme une non-type. Cela aurait l'effet indésirable que chaque nom Foo est dépendant, et donc chaque type utilisé dans Foo (à l'exception des types intégrés) doit être qualifié. À l'intérieur de Toto, vous devriez écrire:
parce que
std::string
serait dépendante de nom, et donc supposé être un non-type, sauf indication contraire. Ouch!Un deuxième problème en permettant à votre choix de code (
return x;
), c'est que même siBar
est défini avantFoo
, etx
n'est pas un membre dans cette définition, quelqu'un pourrait définir ultérieurement une spécialisation deBar
pour certains type deBaz
, tels queBar<Baz>
a un membre de donnéesx
, puis instancierFoo<Baz>
. Donc, dans cette instanciation, votre modèle serait de retour le membre de données au lieu de retourner le mondialx
. Ou à l'inverse, si le modèle de base définition deBar
avaitx
, ils pourraient définir une spécialisation sans elle, et votre modèle de chercher un mondialx
de retour dansFoo<Baz>
. Je pense que cela a été jugé tout aussi surprenante et pénible car le problème que vous avez, mais c'est silencieusement surprenant, par opposition à jeter un surprenant d'erreur.Pour éviter ces problèmes, la norme en vigueur dit que dépendant de la base de catégories de modèles de classe ne sont pas recherchés pour les noms, sauf si les noms sont déjà dépendants pour une autre raison. Cela s'arrête tout d'être dépendants simplement parce qu'il pourrait être trouvé dans une situation de dépendance de la base. Il a aussi l'effet indésirable que vous voyez - vous devez préciser des trucs à partir de la classe de base ou il ne l'est pas. Il y a trois façons de faire de
A
dépendante:using Bar<T>::A;
dans la classe -A
maintenant se réfère à quelque chose dansBar<T>
, donc dépendante.Bar<T>::A *x = 0;
au point d'utilisation - Encore une fois,A
est certainement dansBar<T>
. C'est la multiplication depuistypename
n'était pas utilisé, peut-être un mauvais exemple, mais nous allons devoir attendre jusqu'à ce que l'instanciation de savoir sioperator*(Bar<T>::A, x)
renvoie une rvalue. Qui sait, peut-être qu'il fait...this->A;
au point d'utilisation -A
est membre, si ce n'est pas dansFoo
, il doit être dans la classe de base, de nouveau, la norme dit que cela le rend dépendant.Deux-phase de compilation est délicat et difficile, et introduit quelques surprenant exigences supplémentaires pour le verbiage dans votre code. Mais plutôt comme la démocratie, c'est probablement la pire façon possible de faire des choses, à l'exception de tous les autres.
Vous pourriez raisonnablement faire valoir que, dans votre exemple,
return x;
n'a pas de sens six
est un type imbriqué dans la classe de base, de sorte que la langue doit (a) dire que c'est une personne à charge nom et (2) de la traiter comme un non-type, et votre code devrait fonctionner sansthis->
. Pour une mesure, vous êtes victime de dommages collatéraux de la solution à un problème qui ne s'applique pas dans votre cas, mais il y a toujours le problème de votre classe de base en introduisant potentiellement sous les noms vous que l'ombre globals, ou ne pas avoir des noms que vous pensiez qu'ils avaient, et un être trouvé à la place.Vous pourrait aussi argumenter que la valeur par défaut devrait être le contraire de la dépendance des noms (à supposer, sauf en quelque sorte être un objet), ou que la valeur par défaut devrait être plus sensible au contexte (dans
std::string s = "";
,std::string
pourrait être lu comme un type, puisque rien d'autre, grammaire du sens, même sistd::string *s = 0;
est ambigu). Encore une fois, je ne sais pas tout à fait la façon dont les règles ont été approuvées. Ma conjecture est que le nombre de pages de texte qui serait nécessaire, atténué contre la création d'un lot de règles spécifiques pour lesquels les contextes de prendre un type, un non-type.est-il une telle chose comme le C++QTWBFAETYNSYEWTKTAAHMITTBGOW - "des Questions qui pourraient être posées fréquemment à l'exception que vous n'êtes pas sûr de vous-même envie de connaître la réponse et ont des choses plus importantes à mener avec"?
réponse extraordinaire, me demande si la question pouvait entrer dans la faq.
Waouh, peut-on dire encyclopédique? highfive Un point subtil, mais: "Si Toto est instancié par un type qui a un membre de données Une à la place d'une étude de type A, c'est une erreur dans le code fait l'instanciation (phase 2), et non pas une erreur dans le modèle (phase 1)." Peut-être mieux de dire que le modèle n'est pas incorrect, mais cela pourrait encore être une affaire de faux ou de la logique de bug sur la partie du modèle de l'écrivain. Si le pavillon de l'instanciation était effectivement le cas d'utilisation, puis le modèle serait une erreur.
Je veux dire que c'est une erreur de compilateur à l'un plutôt que l'autre. C'est le même sens que les deux paragraphes plus tôt, je n'en ai pas indiqué tous de nouveau.
OriginalL'auteur
(à l'Origine une réponse de Jan 10, 2011)
Je pense avoir trouvé la réponse: GCC question: à l'aide d'un membre d'une classe de base qui repose sur un argument de modèle.
La réponse n'est pas spécifique à la gcc.
Mise à jour: En réponse à mmichael commentaire, à partir de la projet de N3337 du C++11 Standard:
Si "parce que la norme dit" compte comme une réponse, je ne sais pas. Nous pouvons maintenant nous demander pourquoi la norme de mandats, mais comme Steve Jessop est excellente réponse et de l'autre, la réponse à cette dernière question est plutôt long et défendable. Malheureusement, quand il s'agit de la Norme C++, il est souvent presque impossible de donner une courte et autonome explication des raisons pour lesquelles la norme mandats quelque chose; cela s'applique à la dernière question.
OriginalL'auteur Ali
La
x
est caché au cours de l'héritage. Vous pouvez afficher par:OriginalL'auteur chrisaycock