Por que a palavra-chave 'modelo' é necessária aqui?

9

Eu tenho o seguinte código:

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

Ao compilar isso com o gcc 9.2 e o clang (9.0), estou recebendo um erro de compilação devido à templateexigência da palavra - chave para a chamada fun. Clang mostra:

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

Não entendo por que o compilador pensa que funé um nome dependente no contexto de f, já que fnão é um modelo em si. Se eu mudar Cpara ser uma classe regular em vez de um modelo, o erro desaparece; no entanto, não vejo por que deveria haver um erro em primeiro lugar, pois Snem fdependemos disso TC.

Curiosamente, o MSVC 19.22 compila isso muito bem.


Nota

Antes de votar para fechar como idiota de Onde e por que tenho que colocar as palavras-chave "modelo" e "nome do tipo"? considere que este é um caso especial em que, mesmo que Sseja realmente um nome dependente, no contexto fdele não seria dependente se não fosse pelo fato de serem membros da instanciação atual.

Martin
fonte
Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Bhargav Rao

Respostas:

10

Considere :

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)é analisado como uma expressão que contém duas operações de comparação <e >, e isso é bom para o 1, mas falha para o 2.

Se isso for alterado para s.template a<0>(i), o item 2 está OK e o item 1 falha. Portanto, a templatepalavra-chave nunca é redundante aqui.

O MSVC é capaz de interpretar a expressão nos s.a<0>(i)dois sentidos no mesmo programa. Mas isso não está correto de acordo com o padrão; cada expressão deve ter apenas uma análise para o compilador lidar.

ecatmur
fonte
Eu ainda não entendo isso completamente. Seu exemplo mostra que, neste caso, você usa uma especialização Cou outra, mas nunca pode instanciar as duas. A templatepalavra-chave aqui ainda é IMHO desnecessária, porque a Sescolha depende de qual Cinstância você instancia. Sem a templatepalavra-chave, você seria capaz de instanciar ambos, e o comportamento de f seria diferente para cada instância.
Martin
2
@ Martin, o ponto é que cada token deve ter apenas uma função sintática no arquivo de origem. Por exemplo, não é permitido que o token <seja um operador de comparação em uma instanciação de modelo e um colchete de ângulo de abertura em outra instanciação. Isso é para garantir que os compiladores possam analisar modelos em um AST (com espaços reservados para os tipos de modelo).
ecatmur
Isso faz sentido. Obrigado!
Martin
7

funpode ou não ser uma função de modelo (ou pode não existir), dependendo do parâmetro de modelo de class C.

Isso porque você pode se especializar S(sem se especializar C):

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

Como o compilador deseja saber se funé ou não um modelo quando olha pela primeira vez class C(antes de substituir o parâmetro do modelo), templateé necessário.

HolyBlackCat
fonte
11
mente ... soprado ...
bolov 04/02
O acompanhamento para isso, então, é se essas novas definições Spodem ser acessadas f. Se não puderem, ter essa restrição não faz sentido, porque fnão será capaz de vê-las de qualquer maneira.
Martin
@ Martin Com GCC, Clang e MSVC f, encontra-o.
HolyBlackCat
@bolov Sim, você pode se especializar em muitas coisas diferentes .
HolyBlackCat