Étant donné le modèle de classe suivant:
template<typename T>
struct Outer
{
struct Inner;
auto f(Inner) -> void;
};
nous définissons Inner
séparément pour chaque spécialisation Outer
:
template<>
struct Outer<int>::Inner {};
template<>
struct Outer<double>::Inner {};
puis définissez la fonction membre f
une fois pour toutes les spécialisations de Outer
:
auto Outer<T>::f(Inner) -> void
{
}
mais Clang (9.0.0) se plaint:
error: variable has incomplete type 'Outer::Inner'
auto Outer<T>::f(Inner) -> void
^
Nous pouvons éviter l'erreur de compilation en fournissant également une définition de Inner
toutes les autres spécialisations de Outer
:
template<typename T>
struct Outer<T>::Inner {};
ou en définissant f
séparément pour chaque spécialisation:
template<>
auto Outer<int>::f(Inner) -> void
{
}
template<>
auto Outer<double>::f(Inner) -> void
{
}
GCC et MSVC acceptent le code initial, ce qui pose la question; est-ce un bug Clang ou est-ce la seule implémentation conforme des trois?
Inner
pour toutes les autres spécialisations et la définitionf
séparée pour chaque spécialisation résolvent l'erreur de compilation.Inner
est incomplet malgré les définitions de chaque spécialisationOuter
. Clairement, ceInner
sera (correctement) un type incomplet si vous supprimez sa ou ses définitions.Réponses:
Je crois que Clang a tort de rejeter votre code. Nous devons nous demander, comment votre déclaration de fonction et votre définition se comparent-elles à
Dans cet exemple,
T::Inner
est évidemment un type dépendant. Clang ne peut donc pas supposer qu'il est incomplet jusqu'à l'instanciation. Est-ce la même chose dans votre exemple? Je dirais que oui. Car nous avons ceci dans la norme:La première puce du paragraphe 9 couvre donc l'affaire
typename T::Inner
. C'est un type dépendant.Pendant ce temps, votre cas est couvert par la deuxième puce.
Outer::Inner
est un nom qui se trouve dans l'instanciation actuelle deOuter
, d'ailleurs il se trouve à l'intérieur deOuter
lui-même, et non dans une classe de base. Cela en fait un membre dépendant de l'instanciation actuelle. Ce nom fait référence à une classe imbriquée. Ce qui signifie que toutes les conditions de la deuxième puce s'appliquent, ce qui en fait égalementOuter::Inner
un type dépendant!Puisque nous avons nous-mêmes un type dépendant dans les deux cas, les compilateurs doivent les traiter également comme des types dépendants. Ma conclusion est que GCC et MSVC ont raison.
la source