C ++ - Pourquoi le mot-clé 'template' est-il requis ici?

9

J'ai le code suivant:

template <typename TC>
class C
{
    struct S
    {
        template <typename TS>
        void fun() const
        {}
    };

    void f(const S& s)
    {
        s.fun<int>();
    }
};

// Dummy main function
int main()
{
    return 0;
}

Lors de la construction de ceci avec gcc 9.2 et clang (9.0), j'obtiens une erreur de compilation en raison du templatemot clé requis pour l'invocation fun. Clang montre:

error: use 'template' keyword to treat 'fun' as a dependent template name
        s.fun<int>();
          ^
          template 

Je ne comprends pas pourquoi le compilateur pense que func'est un nom dépendant dans le contexte de f, car ce fn'est pas un modèle lui-même. Si je change Cpour être une classe régulière au lieu d'un modèle, l'erreur disparaît; cependant, je ne vois pas pourquoi il devrait y avoir une erreur en premier lieu puisque ni Sni fdépendre TC.

Curieusement, MSVC 19.22 compile cela très bien.


Remarque

Avant de voter pour fermer en tant que dupe de Où et pourquoi dois-je mettre les mots-clés "modèle" et "nom de type"? veuillez considérer qu'il s'agit d'un cas spécial où même s'il Ss'agit bien d'un nom dépendant, dans le contexte de fcelui-ci ne serait pas dépendant sinon du fait qu'ils sont membres de l'instanciation actuelle.

Martin
la source
Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Bhargav Rao

Réponses:

10

Considérez :

template<typename T>
struct C
{
    struct S
    {
        int a = 99;
    };

    void f(S s, int i)
    {
        s.a<0>(i);
    }
};

template<>
struct C<long>::S
{
    template<int>
    void a(int)
    {}
};

int main()
{
    C<int>{}.f({}, 0); // #1
    C<long>{}.f({}, 0); // #2
}

s.a<0>(i)est analysé comme une expression contenant deux opérations de comparaison <et >, et cela convient pour # 1 mais échoue pour # 2.

Si cela est changé en s.template a<0>(i)alors # 2 est OK et # 1 échoue. Ainsi, le templatemot clé n'est jamais redondant ici.

MSVC est capable d'interpréter l'expression dans les s.a<0>(i)deux sens au sein du même programme. Mais ce n'est pas correct selon la norme; chaque expression ne doit avoir qu'une seule analyse pour le compilateur.

ecatmur
la source
Je ne comprends toujours pas cela. Votre exemple montre que dans ce cas, vous utilisez une spécialisation de Cou l'autre, mais vous ne pouvez jamais instancier les deux. Le templatemot-clé ici n'est toujours pas nécessaire à mon humble avis, car celui qui Sest choisi dépend de celui que Cvous instanciez. Sans le templatemot clé, vous seriez en mesure d'instancier les deux, et le comportement de f serait différent pour chaque instance.
Martin
2
@Martin, le fait est que chaque jeton ne doit avoir qu'un seul rôle syntaxique dans le fichier source. Par exemple, il n'est pas acceptable que le jeton <soit un opérateur de comparaison dans une instanciation de modèle et un support d'angle d'ouverture dans une autre instanciation. Cela permet de garantir que les compilateurs peuvent analyser les modèles vers un AST (avec des espaces réservés pour les types de modèles).
ecatmur
Ça a du sens. Merci!
Martin
7

funpeut ou peut ne pas être une fonction de modèle (ou peut ne pas exister du tout) selon le paramètre de modèle de class C.

C'est parce que vous pouvez vous spécialiser S(sans vous spécialiser C):

template <> struct C<int>::S {};

Parce que le compilateur veut savoir s'il funs'agit d'un modèle ou non lors de la première consultation class C(avant de remplacer le paramètre de modèle), templateest requis.

HolyBlackCat
la source
1
esprit ... soufflé ...
bolov
La suite de cela est donc de savoir si ces nouvelles définitions de Sseront accessibles par f. S'ils ne le peuvent pas, cette restriction n'a aucun sens car fils ne pourront pas les voir de toute façon.
Martin
@Martin Avec GCC, Clang et MSVC le ftrouve.
HolyBlackCat
@bolov Oui, vous pouvez spécialiser beaucoup de choses différentes .
HolyBlackCat