Parce que quand il y a un paramètre du modèle impliqué, decltype
renvoie un unqiue type de charge selon la norme, voir ci-dessous. Si il n'y a pas de paramètre de modèle puis il se résout à un évident size_t
. Dans ce cas, vous avez à choisir, soit la déclaration et la définition est indépendante de l'expression (par exemple, size_t/decltype(sizeof(int))
), un type de retour, ou les deux ont dépendante de l'expression (par exemple, decltype(sizeof(T))
), qui s'est résolue à un unique type de charge et considéré comme l'équivalent, si leurs expressions sont équivalentes (voir ci-dessous).
Dans ce post, je suis en utilisant le standard C++ projet de N3337.
§ 7.1.6.2 [dcl.type.simpl]
¶ 4
Le type indiqué par decltype(e) est définie comme suit:
- si e est un sans parenthèse id-expression ou un sans parenthèse membre de la classe d'accès (5.2.5), decltype(e)
est le type de l'entité nommée par courriel. Si il n'y a pas une telle entité, ou si e désigne un ensemble de surcharge func-
tions, le programme est mal formé;
- sinon, si e est un xvalue, decltype(e) est en T&&, où T est le type de e;
- sinon, si e est une lvalue, decltype(e) est en T&, où T est le type de e;
- sinon, decltype(e) est le type de l'e.
C'est ce qui explique qu'est - decltype(sizeof(int))
. Mais pour l' decltype(sizeof(T))
il y a un autre article expliquant ce que c'est.
§ 14.4 [temp.type]
¶ 2
Si une expression e implique un paramètre du modèle, decltype(e) désigne un unique type de charge. Deux de ces
decltype spécificateurs de se référer au même type que si leurs expressions sont équivalentes (14.5.6.1). [ Note: toutefois,
il peut être un alias, par exemple, par une définition de type-nom. - la note de fin ]
Dans Clang LLVM sources de la version 3.9 dans le fichier lib/AST/Type.cpp
DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
// C++11 [temp.type]p2: "If an expression e involves a template parameter,
// decltype(e) denotes a unique dependent type." Hence a decltype type is
// type-dependent even if its expression is only instantiation-dependent.
: Type(Decltype, can, E->isInstantiationDependent(),
E->isInstantiationDependent(),
E->getType()->isVariablyModifiedType(),
E->containsUnexpandedParameterPack()),
La phrase importante qui commence comme "d'Où un decltype...". Nouveau clarifie la situation.
De nouveau dans Clang sources de la version 3.9 dans le fichier lib/AST/ASTContext.cpp
QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const {
DecltypeType *dt;
// C++11 [temp.type]p2:
// If an expression e involves a template parameter, decltype(e) denotes a
// unique dependent type. Two such decltype-specifiers refer to the same
// type only if their expressions are equivalent (14.5.6.1).
if (e->isInstantiationDependent()) {
llvm::FoldingSetNodeID ID;
DependentDecltypeType::Profile(ID, *this, e);
void *InsertPos = nullptr;
DependentDecltypeType *Canon
= DependentDecltypeTypes.FindNodeOrInsertPos(ID, InsertPos);
if (!Canon) {
// Build a new, canonical typeof(expr) type.
Canon = new (*this, TypeAlignment) DependentDecltypeType(*this, e);
DependentDecltypeTypes.InsertNode(Canon, InsertPos);
}
dt = new (*this, TypeAlignment)
DecltypeType(e, UnderlyingType, QualType((DecltypeType *)Canon, 0));
} else {
dt = new (*this, TypeAlignment)
DecltypeType(e, UnderlyingType, getCanonicalType(UnderlyingType));
}
Types.push_back(dt);
return QualType(dt, 0);
}
Donc, vous voyez Clang réunit et choisit ces types de charge de l' decltype
) à partir d'une série spéciale.
Pourquoi le compilateur est tellement stupide qu'il ne voit pas que l'expression de l' decltype
est sizeof(T)
toujours size_t
? Oui, c'est évident pour un lecteur humain. Mais lors de la conception et de mettre en œuvre une grammaire formelle et sémantique des règles, en particulier pour les complexes tels langages comme C++, vous devez regrouper des problèmes et définir des règles pour eux, plutôt que de simplement venir avec une règle pour chaque problème particulier, dans la façon dont vous simplement ne sera pas en mesure de se déplacer avec votre langue/conception du compilateur. La même ici, il n'y a pas de règle: si decltype
a un appel de fonction, expression qui n'a pas besoin de paramètres du modèle de résolution de résoudre decltype
pour le type de retour de la fonction. Il n'y a plus que cela, il ya tellement de nombreux cas, vous avez besoin pour couvrir, que vous venez avec un plus générique de la règle, à l'instar de la cité au-dessus de la norme (14.4[2]
).
En outre, un même type de non-évidente cas auto
, decltype(auto)
trouvé par AndyG en C++-14 (N4296, § 7.1.6.4 [dcl.spec.auto], 12/13):
§ 7.1.6.4 [dcl.spec.auto]
¶ 13
Redeclarations ou spécialisations d'une fonction ou d'une fonction de modèle avec une déclaration de type de retour qui utilise un
espace réservé type doit aussi utiliser l'espace réservé, pas une déduit type. [ Exemple:
auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()
decltype(auto) f(); // error, auto and decltype(auto) don't match
Les changements en C++17, Numéro de Document >= N4582
Changement dans le projet de norme N4582 à partir de Mars 2016 (grâce à bogdan) généralise l'énoncé:
§ 17.4 (ancien § 14.4) [temp.type]
¶ 2
Si une expression e est dépendant du type de (17.6.2.2), decltype(e) désigne un unique type de charge. Deux de ces
decltype spécificateurs de se référer au même type que si leurs expressions sont équivalentes (17.5.6.1). [ Note: toutefois,
ce type peut être un alias, par exemple, par une définition de type-nom. - la note de fin ]
Ce changement conduit à une autre section décrivant le type dépendante de l'expression qui semble étrange à notre cas particulier.
§ 17.6.2.2 [temp.dep.expr] (ancien § 14.6.2.2)
¶ 4
Les Expressions de l'un des formulaires suivants sont jamais dépendant du type (parce que le type de l'expression ne peut pas être
charge):
...
sizeof ( type-id )
...
Il y a d'autres sections sur la valeur dépendante des expressions où l' sizeof
peut être une valeur dépendant si l' type-id
- dépendants. Il n'y a aucun rapport entre la valeur dépendante de l'expression et de l' decltype
. Après réflexion, je n'ai trouvé aucune raison de s' decltype(sizeof(T))
ne doit pas ou ne peut pas résoudre en size_t
. Et je suppose que c'était assez sournois changement ("comporte un paramètre de modèle" à "dépendant du type") dans la norme que les développeurs de compilateurs ne paient pas beaucoup d'attention à (peut-être submergé par de nombreux autres changements, peut-être ne pense pas qu'il pourrait réellement changer quelque chose, juste une simple formulation d'amélioration). Le changement ne fait sens, parce qu' sizeof(T)
n'est pas dépendant du type, c'est la valeur-dépendante. decltype(e)
s'opérande est un non évaluée opérande, c'est à dire ne se soucie pas de valeur, uniquement sur le type. C'est pourquoi, decltype
renvoie un type unique uniquement lorsqu' e
est dépendant du type. sizeof(e)
seulement de la valeur-dépendante.
J'ai essayé le code avec clang 5, gcc 8 -std=c++1z
- le même résultat: erreur. Je suis allé plus loin et a essayé ce code:
template <typename>
struct Cls {
static std::size_t f();
};
template <typename T>
decltype(sizeof(sizeof(T))) Cls<T>::f() {
return 0;
}
La même erreur a été donné, de même qu' sizeof(sizeof(T))
n'est ni de type ou de la valeur-dépendante (voir ce post). Cela me donne une raison de supposer que les compilateurs de travail dans une ancienne façon de C++-11/14 standard (c'est à dire "comporte un paramètre de modèle") comme dans la source de l'extrait ci-dessus de clang 3,9 source (je peux vérifier que le dernier développement de clang 5.0 a les mêmes lignes, je n'ai rien trouvé sur ce nouveau changement dans la norme), mais pas en fonction du type d'.