web-dev-qa-db-fra.com

Comment les paramètres de modèle de type variadic et les paramètres de modèle de type de type du modèle de modèle de modèle d'une classe imbriquée se contrarient-ils?

Considérons la structure suivante: S est un modèle de classe avec un paquet variable de paramètres de type de modèle ...Ts. La classe contient un modèle de classe imbriquée N, qui possède un seul paramètre de modèle de modèle C. C elle-même est amplifiée avec un paquet variable de paramètres de modèle non de type qui ont exactement les types Ts....

template <typename ...Ts>
struct S 
{
    template <template <Ts...> typename C>
    struct N 
    {
        C<42> c;
    };
};

GCC rejette la déclaration de c avec:

error: expansion pattern '<anonymous>' contains no parameter packs
        C<42> c;
            ^

Je ne sais pas ce que ce diagnostic signifie et je suppose que c'est un bug de GCC.


Clang accepte cette structure et accepte également une instanciation raisonnable telle que:

template<int> struct W {};
S<int>::N<W> w;             // ok, makes sense

Bien sûr, la Déclaration C<42> c; elle-même lie les contraintes sur le modèle utilisé comme argument pour le paramètre C, qui sera requis même si le Ts... dans C étaient simplement auto.... par exemple. le premier argument de Ts... Doit être un type implicitement convertible d'un int. En outre, les paramètres restants dans Ts..., le cas échéant, doit avoir des valeurs par défaut.

template<bool(*)()> struct X1 {};
S<int>::N<X1> x1;                  // error: value of type 'int' is not implicitly convertible to 'bool (*)()'
        
template<int, char> struct X2 {};
S<int>::N<X2> x2;                  // error: too few template arguments for class template 'X2'

Fait intéressant, il semble y avoir des contraintes imposées par la relation entre ...Ts et Ts.... par exemple. Tous les arguments spécifiés pour S doivent désormais être valides types de paramètres de modèle de modèle non-type:

template<int> struct Y {};
S<int, void>::N<Y> y;       // error: a non-type template parameter cannot have type 'void' 

D'autre part, Clang accepte également les instanciations avec des arguments apparemment incompatibles pour les paramètres S et des paramètres de modèle de type pour C:

template<int> struct Z {};
S<bool(*)(), bool>::N<Z> z;  // ok ?? But why? both number and type of 'Ts' is different 

Voici A Demo .

Alors, quelles sont les règles pour comment ...Ts et Ts... Se contraindre dans cette structure?


Je suis tombé sur cette question tout en essayant de comprendre cela question où il est indiqué que MSVC n'accepte pas le code également.

15
cigien

Pour votre principale question (classe imbriquée avec des arguments de modèle sans type dépendant des arguments de modèle en classe extérieure) Il s'agit d'un bogue dans GCC, # 8688 (voir Commentaire n ° )

4
AndyG

Je pense que Clang n'a pas raison.
Temp.param # 15

Un pack de paramètres de modèle qui est une déclaration de paramètre dont le type contient un ou plusieurs packs de paramètres non exhalées est une expansion de pack.

Pour le modèle de modèle de modèle de la classe de modèle imbriquée N, c'est template <Ts...> typename C, Où Ts... Est une instanciation de paquets, qui a la règle suivante:

Chaque EI est généré en instanciant du motif et en remplaçant chaque paramètre d'expansion de paquet avec son élément. Un tel élément, dans le contexte de l'instanciation, est interprété comme suit:

  • si le pack est un pack de paramètres de modèle, l'élément est un paramètre de modèle du type correspondant (type ou non de type) désignant le type ou la valeur de l'argument de modèle;

Donc, pour cet exemple S<bool(*)(), bool>::N<Z> z;, instancez Ts... Donnerait une liste bool(*)(), bool. Par conséquent, la question peut être simplifiée pour:

template<template<bool(*)(), bool> class C>
struct Nested{};
template<int> struct Z {};
int main(){
  Nested<Z> z; // is well-formed?
}

Par - temp.arg.template #

Un argument-argument correspond à un modèle de modèle-paramètre p lorsque p est au moins aussi spécialisé que le modèle-argument A.

L'argument Z correspond-il au paramètre C? Selon cette règle temp.arg.template # 4

Un modèle de modèle-paramètre p est au moins aussi spécialisé qu'un modèle de modèle-argument A si, étant donné la réécriture suivante à deux modèles de fonction, le modèle de fonction correspondant à P est au moins aussi spécialisé que le modèle de fonction correspondant à un Règles de commande partielle pour les modèles de fonctions. Compte tenu d'un modèle de classe inventé x avec la liste des paramètres de modèle d'A (, y compris les arguments par défaut ):

  • Chacun des deux modèles de fonctions a les mêmes paramètres de modèle, respectivement, comme p ou A.
  • Chaque modèle de fonction comporte un paramètre de fonction unique dont le type est une spécialisation de X avec des arguments de modèle correspondant aux paramètres de modèle à partir du modèle de fonction correspondant où, pour chaque paramètre de modèle PP dans la liste des paramètres de modèle de Le modèle de fonction, un argument de modèle correspondant AA est formé. Si PP déclare un paquet de paramètres, puis aa est l'expansion de pack pp ... ([Temp.variadic]); sinon, aa est l'id-expression pp.

Si la réécriture produit un type invalide, p n'est pas au moins aussi spécialisé que A.

Cela signifie que nous avons une classe de modèle inventée X a la forme:

template<int>
struct InventedX{};

Ensuite, le modèle de fonction réécrit pour A a la forme:

template<int N>
auto iventend_function(X<N>);

tandis que le modèle de fonction réécrit pour P a la forme:

template<bool(*A)(), bool B>
auto invented_function(X<A,B>) // invalid type for X<A,B>

Donc, p n'est pas au moins aussi spécialisé que A . Par conséquent, A ne peut pas correspondre à P. Donc, S<bool(*)(), bool>::N<Z> z; doit être mal formée.

2
jack X