C ++ - Perché la parola chiave 'template' è richiesta qui?


9

Ho il codice seguente:

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;
}

Quando lo costruisco con gcc 9.2 e clang (9.0), ricevo un errore di compilazione a causa della templateparola chiave richiesta per invocare fun. Clang mostra:

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

Non capisco perché il compilatore pensi funsia un nome dipendente nel contesto di f, dal momento che fnon è un modello stesso. Se cambio Cad essere una classe normale anziché un modello, l'errore scompare; tuttavia, non vedo perché ci dovrebbe essere un errore in primo luogo poiché né Sfdipende TC.

Stranamente, MSVC 19.22 lo compila bene.


Nota

Prima di votare per chiudere come duplicato di Dove e perché devo inserire le parole chiave "template" e "typename"? si prega di considerare che questo è un caso speciale in cui anche se Sè effettivamente un nome dipendente, nel contesto di fesso non sarebbe dipendente se non per il fatto che sono membri della presente istanza.


I commenti non sono per una discussione estesa; questa conversazione è stata spostata in chat .
Bhargav Rao

Risposte:


10

Considera :

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)viene analizzato come un'espressione contenente due operazioni di confronto <e >, e questo va bene per il n. 1 ma fallisce per il n. 2.

Se questo viene modificato in s.template a<0>(i)# 2 è OK e # 1 non riesce. Pertanto la templateparola chiave non è mai ridondante qui.

MSVC è in grado di interpretare l'espressione in s.a<0>(i)entrambi i modi all'interno dello stesso programma. Ma questo non è corretto secondo lo standard; ogni espressione dovrebbe avere una sola analisi da gestire per il compilatore.


Non lo capisco ancora del tutto. Il tuo esempio mostra che in questo caso o usi una specializzazione Co l'altra, ma non puoi mai istanziare entrambe. La templateparola chiave qui è ancora IMHO non necessaria, perché la Sscelta viene presa in base Call'istanza. Senza la templateparola chiave saresti in grado di creare un'istanza di entrambi e il comportamento di f differirebbe per ogni istanza.
Martin,

2
@Martin il punto è che ogni token dovrebbe avere un solo ruolo sintattico nel file di origine. Ad esempio, non è corretto che il token <sia un operatore di confronto in un'istanza di modello e una parentesi angolare di apertura in un'altra istanza. Questo per garantire che i compilatori possano analizzare i modelli in un AST (con segnaposto per i tipi di modello).
ecatmur,

Questo ha senso. Grazie!
Martin,

7

funpuò o meno essere una funzione modello (o potrebbe non esistere affatto) a seconda del parametro modello di class C.

Questo perché puoi specializzarti S(senza specializzarti C):

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

Perché il compilatore vuole sapere se funè un modello o meno al primo sguardo class C(prima di sostituire il parametro template), templateè necessario.


1
mente ... soffiato ...
Bolov

Il seguito di questo, quindi, è se queste nuove definizioni di Spotranno mai essere raggiunte f. Se non possono, avere questa restrizione non ha senso perché fnon sarà in grado di vederli comunque.
Martin,

@Martin Con GCC, Clang e MSVC flo trovano.
HolyBlackCat

@bolov Sì, puoi specializzare molte cose diverse .
HolyBlackCat
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.