41 votes

Pourquoi le modèle d'alias donne-t-il une déclaration contradictoire ?

Le portage d'une partie du code C++11 de Clang vers g++

template<class T>
using value_t = typename T::value_type;

template<class>
struct S
{
    using value_type = int;
    static value_type const C = 0;
};

template<class T> 
value_t<S<T>> // gcc error, typename S<T>::value_type does work
const S<T>::C;

int main() 
{    
    static_assert(S<int>::C == 0, "");
}

donne un comportement différent pour Clang (versions 3.1 à SVN trunk) et pour n'importe quelle version de g++. Pour cette dernière, j'obtiens les erreurs suivantes comme ceci

prog.cc:13:13: error: conflicting declaration 'value_t<S<T> > S< <template-parameter-1-1> >::C'
 const S<T>::C;
             ^
prog.cc:8:29: note: previous declaration as 'const value_type S< <template-parameter-1-1> >::C'
     static value_type const C = 0;
                             ^
prog.cc:13:13: error: declaration of 'const value_type S< <template-parameter-1-1> >::C' outside of class is not definition [-fpermissive] const S<T>::C;

Si, au lieu de l'alias de modèle value_t<S<T>> J'utilise l'intégralité de la typename S<T>::value_type entonces g++ fonctionne également .

Question Les alias de modèles ne sont-ils pas censés être complètement interchangeables avec leur expression sous-jacente ? Est-ce un bug de g++ ?

Mise à jour : Visual C++ accepte également le modèle d'alias dans la définition hors classe.

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.

5voto

Mário Feroldi Points 1918

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 foos'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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X