C++11
Problème
Bien que les règles en C++03 sujet de quand vous avez besoin d' typename
et template
sont en grande partie raisonnable, il y a un inconvénient gênant de sa formulation
template<typename T>
struct A {
typedef int result_type;
void f() {
// error, "this" is dependent, "template" keyword needed
this->g<float>();
// OK
g<float>();
// error, "A<T>" is dependent, "typename" keyword needed
A<T>::result_type n1;
// OK
result_type n2;
}
template<typename U>
void g();
};
Comme vous pouvez le voir, nous avons besoin de la désambiguïsation mot-clé, même si le compilateur peut parfaitement comprendre lui-même qu' A::result_type
ne peuvent être int
(et est donc un type), et de l' this->g
ne peut être que le membre modèle g
déclaré plus tard (même si A
est explicitement spécialisés quelque part, cela ne modifie pas le code au sein de ce modèle, de sorte que son sens ne peut pas être affectée par un plus tard, la spécialisation de l' A
!).
Courant de l'instanciation
Pour améliorer la situation, en C++11 la langue des pistes quand un type se réfère à l'enfermant modèle. À savoir que, le type doit avoir été formé en utilisant une certaine forme de nom, qui est son propre nom (ci-dessus, A
, A<T>
, ::A<T>
). Un type référencé par un nom qui est connu pour être le courant de l'instanciation. Il peut y avoir plusieurs types qui sont tous au courant de l'instanciation si le type de qui le nom est formé, est un membre/classe imbriquée (puis, A::NestedClass
et A
sont à la fois actuel instanciations).
Basé sur cette notion, la langue dit qu' CurrentInstantiation::Foo
, Foo
et CurrentInstantiationTyped->Foo
(comme A *a = this; a->Foo
) sont tous membres de l'actuel instanciation si elles sont membres d'une classe qui est l'actuel instanciation ou de l'une de ses non-dépendante des classes de base (il vous suffit de faire la recherche par nom immédiatement).
Les mots clés typename
et template
sont maintenant pas plus requis si le qualificatif est un membre de l'actuel de l'instanciation. Un point crucial de rappeler ici est que l' A<T>
est encore un de dépendant du type de nom (après tout, T
est également dépendante du type). Mais A<T>::result_type
est connu pour être de type, le compilateur "comme par magie" regarde ce genre de types dépendants.
struct B {
typedef int result_type;
};
template<typename T>
struct C { }; // could be specialized!
template<typename T>
struct D : B, C<T> {
void f() {
// OK, member of current instantiation!
// A::result_type is not dependent: int
D::result_type r1;
// error, not a member of the current instantiation
D::questionable_type r2;
// OK for now - relying on C<T> to provide it
// But not a member of the current instantiation
typename D::questionable_type r3;
}
};
C'est impressionnant, mais que pouvons-nous faire mieux? La langue va même plus loin et exige qu'une mise en œuvre regarde à nouveau jusqu' D::result_type
lors de l'instanciation D::f
(même si il a trouvé son sens déjà à la définition du temps). Lorsque maintenant le résultat de recherche diffère ou les résultats dans l'ambiguïté, le programme est mal formé et un diagnostic doit être donné. Imaginez ce qui arriverait si nous avons défini C
comme ceci
template<>
struct C<int> {
typedef bool result_type;
typedef int questionable_type;
};
Un compilateur est nécessaire à la capture de l'erreur lors de l'instanciation D<int>::f
. Ainsi, vous obtenez le meilleur des deux mondes: "le Retard" recherche de vous protéger si vous pourriez avoir de la difficulté à charge des classes de base, et aussi "Immédiate" de recherche qui vous libère typename
et template
.
Inconnu spécialisations
Dans le code de l' D
, le nom de l' typename D::questionable_type
n'est pas un membre de l'actuel de l'instanciation. Au lieu de la langue de la marque comme un membre d'une inconnue de spécialisation. En particulier, ce qui est toujours le cas lorsque vous effectuez DependentTypeName::Foo
ou DependentTypedName->Foo
et soit le type de charge n'est pas au courant de l'instanciation (dans ce cas, le compilateur peut faire et de dire "nous allons examiner plus tard ce qu' Foo
) ou il est le courant de l'instanciation et le nom n'a pas été trouvé ou ses non-dépendante des classes de base et il y a aussi dépendante des classes de base.
Imaginez ce qui arriverait si nous avions une fonction membre h
dans le ci-dessus définis A
modèle de classe
void h() {
typename A<T>::questionable_type x;
}
En C++03, la langue a permis de rattraper cette erreur, car il ne pourrait jamais être un moyen valable pour instancier A<T>::h
(quel que soit l'argument que vous donnez à T
). En C++11, la langue a maintenant un chèque supplémentaire de donner une raison de plus pour les compilateurs pour mettre en œuvre cette règle. Depuis A
a la charge des classes de base, et A
déclare aucun membre de l' questionable_type
, le nom de l' A<T>::questionable_type
est ni un membre de l'actuel de l'instanciation , ni un membre d'un inconnu spécialisation. Dans ce cas, il devrait y avoir aucun moyen que ce code pourrait valablement compiler à l'instanciation de temps, de sorte que la langue interdit à un nom de où le qualificatif est un courant de l'instanciation de n'être ni un membre d'un inconnu spécialisation, ni membre de l'actuel de l'instanciation (toutefois, cette violation est toujours pas nécessaire d'être diagnostiqué).
Les exemples et les anecdotes
Vous pouvez essayer cette connaissance de cette réponse et de voir si les définitions ci-dessus ont un sens pour vous sur un exemple réel (ils se répètent un peu moins détaillée dans la réponse).
Le C++11, les règles, les valide en C++03 code mal formé (qui n'était pas prévue par le comité C++, mais ne sera probablement pas fixe)
struct B { void f(); };
struct A : virtual B { void f(); };
template<typename T>
struct C : virtual B, T {
void g() { this->f(); }
};
int main() {
C<A> c; c.g();
}
Cela valide en C++03 code lier this->f
de A::f
lors de l'instanciation de temps et tout va bien. C++11 est cependant immédiatement le lie à B::f
et nécessite un double-vérifier lors de l'instanciation, de vérifier si le recherche encore des matches. Cependant lors de l'instanciation C<A>::g
, la Domination de la Règle s'applique et la recherche d'trouverez A::f
à la place.