Le problème repose sur SFINAE. Si vous réécrivez votre membre de la fonction d' value_t<S<T>>
, comme à l'extérieur de la déclaration, puis GCC sera heureux de le compiler:
template<class T>
struct S
{
using value_type = int;
static const value_t<S<T>> C = 0;
};
template<class T>
const value_t<S<T>> S<T>::C;
Parce que l'expression est maintenant fonctionnellement équivalent. Des choses comme la substitution de l'échec entrent en jeu sur les alias-modèles, mais comme vous le voyez, la fonction de membre value_type const C
n'ont pas la même "prototype" en tant que value_t<S<T>> const S<T>::C
. D'abord on n'a pas à effectuer SFINAE, tandis que le second exige. Donc clairement, les deux déclarations ont des fonctionnalités différentes, donc du CCG crise de colère.
Il est intéressant de noter, Clang compile sans un signe d'anomalie. Je suppose qu'il se trouve que l'ordre de Clang les analyses sont inversées par rapport à du CCG. Une fois l'alias-l'expression d'un modèle est résolu et fine (c'est à dire qu'il est bien formé), clang compare ensuite les deux déclarations et vérifier qu'elles sont équivalentes (qui, dans ce cas, ils sont, étant donné les deux expressions résoudre value_type
).
Maintenant, ce qui est correct à partir de la norme yeux? C'est toujours une question non résolue de savoir si envisager des alias du modèle SFNIAE dans le cadre de sa déclaration de fonctionnalité. Citant [temp.alias]/2:
Lorsqu'un modèle-id se réfère à la spécialisation d'un alias de modèle, c'est équivalent au type associé obtenus par substitution de son modèle arguments pour le modèle de paramètres dans le type-id de l'alias de modèle.
En d'autres termes, ces deux sont équivalentes:
template<class T>
struct Alloc { /* ... */ };
template<class T>
using Vec = vector<T, Alloc<T>>;
Vec<int> v;
vector<int, Alloc<int>> u;
Vec<int>
et vector<int, Alloc<int>>
sont des types équivalents, parce que, après la substitution est effectuée, les deux types finissent par être vector<int, Alloc<int>>
. Note comment "après une substitution" signifie que l'équivalence n'est vérifié une fois tous les arguments de modèle sont remplacés par les paramètres du modèle. Qui est, la comparaison commence lorsque T
en vector<T, Alloc<T>>
est remplacé par int
de Vec<int>
. Peut-être que c'est ce que Clang est en train de faire avec value_t<S<T>>
? Mais ensuite il y a la citation suivante de [temp.alias]/3:
Toutefois, si le modèle-id est dépendante, à la suite argument de modèle de substitution s'applique toujours au modèle-id. [Exemple:
template<typename...> using void_t = void;
template<typename T> void_t<typename T::foo> f();
f<int>(); // error, int does not have a nested type foo
fin de l'exemple]
Voici le problème: l'expression est d'être bien formé, de sorte que le compilateur a besoin de vérifier si la substitution est très bien. Quand il y a une dépendance afin d'effectuer argument de modèle de substitution (par exemple, typename T::foo
), la fonctionnalité de l'ensemble des modifications de l'expression, et la définition de l ' "équivalence" est différent. Par exemple, le code suivant ne compile pas (GCC et Clang):
struct X
{
template <typename T>
auto foo(T) -> std::enable_if_t<sizeof(T) == 4>;
};
template <typename T>
auto X::foo(T) -> void
{}
Parce que l'extérieur de la foo
s'prototype est fonctionnellement différent de l'intérieur. Faire auto X::foo(T) -> std::enable_if_t<sizeof(T) == 4>
au lieu de cela rend le code compile bien. C'est donc parce que le type de retour d' foo
est une expression qui dépend de la suite de l' sizeof(T) == 4
, donc après le modèle de substitution, son prototype pourrait être différente de chaque instance. Alors que, auto X::foo(T) -> void
's type de retour n'est jamais différent, ce qui entre en conflit avec la déclaration à l'intérieur d' X
. C'est le même problème qui se passe avec votre code. Donc GCC semble être correct dans ce cas.
4 votes
Il semble bien qu'ils devraient être équivalents : eel.is/c++draft/temp.alias#2
4 votes
J'opterai pour un bogue de compilateur pour 500, Alex.
8 votes
Je ne pense pas que ce soit si insignifiant. Il y a beaucoup de questions sur l'équivalence des types dépendants en ce qui concerne les modèles d'alias. Voir open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1979 et toutes les questions qui y sont liées. Une réponse devrait couvrir cette question et la pertinence de ces questions, IMO.