quelle est la signification de l'expression «initialisation précédente» dans la section [expr.const]
constexpr int func(int const& rf){
return rf;
}
int main(){
constexpr int value = func(0);
}
Considérons le code ci-dessus, la variable valuedoit être initialisée par une expression constante, qui est func(0), tout d'abord, une expression constante de base. Pour déterminer si l'expression func(0)est une expression constante de base, les règles suivantes lui seront appliquées, c'est-à-dire:
expr.const # 2.7
Une expression e est une expression constante de base à moins que l'évaluation de e, suivant les règles de la machine abstraite, évalue l'une des expressions suivantes:
une conversion lvalue-en-rvalue sauf si elle est appliquée à
[...], ou
(2.7.4) une valeur gl non volatile de type littéral qui se réfère à un objet non volatile dont la durée de vie a commencé pendant l'évaluation de e;
Malgré l' lvalue-to-rvalue conversionapplication rfet une telle conversion satisfaite, la puce (2.7.4), cependant, jetez un œil au paragraphe suivant, à savoir:
expr.const # 2.11
une expression id qui fait référence à une variable ou un membre de données de type référence à moins que la référence n'ait une initialisation précédente et soit
- (2.11.1) il est initialisé avec une expression constante ou,
- (2.11.2) sa durée de vie a commencé au cours de l'évaluation de e;
Je ne sais pas ce que preceding initializationsignifie réellement cette phrase ? Cela signifie-t-il qu'une variable doit être initialisée avant de l'utiliser, ou cela signifie que dans une déclaration d'une variable doit avoir un initialiseur. Quoi qu'il en soit, avant d'appliquer la lvalue-to-rvalue conversionà glvalue rf, la glvalue rfdoit être évaluée pour déterminer l'identité d'un objet, qui est régie par:
Une glvalue est une expression dont l'évaluation détermine l'identité d'un objet, d'un champ de bits ou d'une fonction.
Cela signifie que non seulement la puce [expr.const # 2.7] doit être respectée, mais aussi [expr.const # 2.11] doit être respectée.
Parce que id-expression rfest de type référence. Ainsi, pour que l'expression func(0)soit une expression constante de base, l'expression id rfdoit avoir une initialisation précédente , et satisfait au moins l'un des points (2.11.1) et (2.11.2). Dans mon exemple, la puce (2.11.2) est respectée rfet le compilateur convient que func(0)c'est une expression constante. Le résultat est ici , le résultat semble prouver que cela preceding initializationsignifie be initializedplutôt que d'avoir un initialiseur en raison de la déclaration de paramètre ne dispose pas d'un initialiseur dans le premier exemple.
Afin de vérifier une telle pensée, je teste le code ci-dessous:
constexpr int func(int const& rf){
constexpr int const& v = rf;
return 0;
}
int main(){
static int const data = 0;
constexpr int value = func(data);
}
Les résultats des compilateurs soulignent que ce rfn'est pas une expression constante. Je suis confus à ce résultat. Selon la supposition ci-dessus. rfa une initialisation précédente, et la puce (2.11.1)est respectée car datac'est une expression constante, même si la puce (2.11.2)n'est pas satisfaite.
Alors, je me demande ce que signifie réellement cette phrase preceding initialization? Si cela signifie qu'une déclaration pour une variable a un initialiseur, comment l'expression func(0)du premier exemple pourrait-elle être une expression constante?
Réponses
Le résultat semble prouver que «initialisation précédente» signifie «être initialisé» plutôt que d'avoir un initialiseur car la déclaration de paramètre n'a pas d'initialiseur dans le premier exemple.
Cela signifie «être initialisé», mais il s'agit surtout de la visibilité d'une initialisation précédente dans le contexte de l'expression en cours d'évaluation . Dans votre exemple, dans le contexte de l'évaluation, func(0)le compilateur a le contexte pour voir l'initialisation de rfwith 0. Cependant, dans le contexte de l'évaluation uniquement de l'expression à l' rfintérieur func, il ne voit pas d'initialisation de rf. L'analyse est locale, car elle n'analyse pas tous les sites d'appel. Cela conduit à ce que l'expression rfelle-même func ne soit pas une expression constante alors qu'elle func(0)est une expression constante.
Si vous deviez écrire ceci à la place:
constexpr int func(int const& rf) {
/* constexpr */ int const& v = rf;
return v;
}
int main() {
static int const data = 0;
constexpr int value = func(data);
}
Ceci est à nouveau correct car dans le contexte de func(data), rfa une initialisation précédente avec une expression constante data, et va une initialisation précédente avec rflaquelle n'est pas une expression constante mais sa durée de vie a commencé dans l'évaluation de func(data).
C'est CWG2186 :
2186. Point peu clair selon lequel «l'initialisation précédente» doit précéder
Semblable à la préoccupation du problème 2166, l'exigence de 8.20 [expr.const] puce 2.7.1 pour
- une valeur gl non volatile de type intégral ou énumération qui fait référence à un objet const non volatile complet avec une initialisation précédente, initialisé avec une expression constante, ou
ne précise pas le point auquel la détermination de «l'initialisation précédente» est faite: est-ce au point où la référence à la variable apparaît lexicalement, ou est-ce le point où se produit l'évaluation de la constante la plus externe? Il y a divergence de mise en œuvre sur ce point.
Mais le sens devrait être «lexicalement», car ODR.