C ++ - Dlaczego słowo kluczowe „szablon” jest tutaj wymagane?

9

Mam następujący kod:

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

Kiedy buduję to z gcc 9.2 i clang (9.0), pojawia się błąd kompilacji, ponieważ templatesłowo kluczowe jest wymagane do wywołania fun. Clang pokazuje:

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

Nie rozumiem, dlaczego kompilator myśli funw kontekście o nazwie zależnej f, ponieważ fsam nie jest szablonem. Jeśli zmienię Csię na zwykłą klasę zamiast szablonu, błąd zniknie; Jednak nie widzę dlaczego nie powinno być błąd w pierwszej kolejności, ponieważ ani Snie fzależą TC.

Co dziwne, MSVC 19.22 dobrze to kompiluje.


Uwaga

Przed głosowaniem, aby zamknąć jako duplikat Gdzie i dlaczego muszę umieścić słowa kluczowe „szablon” i „typename”? proszę wziąć pod uwagę, że jest to szczególny przypadek, w którym nawet jeśli Sjest to nazwa zależna, w kontekście fnie byłaby zależna, gdyby nie fakt, że są członkami bieżącej instancji.

Jaskółka oknówka
źródło
Komentarze nie są przeznaczone do rozszerzonej dyskusji; ta rozmowa została przeniesiona do czatu .
Bhargav Rao

Odpowiedzi:

10

Zastanów się :

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)jest analizowany jako wyrażenie zawierające dwie operacje porównania <i >, i jest to w porządku dla # 1, ale kończy się niepowodzeniem dla # 2.

Jeśli to zostanie zmienione na, s.template a<0>(i)to nr 2 jest w porządku, a nr 1 kończy się niepowodzeniem. Dlatego templatesłowo kluczowe nigdy nie jest tutaj zbędne.

MSVC jest w stanie interpretować wyrażenie na s.a<0>(i)dwa sposoby w tym samym programie. Ale to nie jest poprawne zgodnie ze Standardem; każde wyrażenie powinno mieć tylko jedną analizę, z którą kompilator sobie poradzi.

ecatmur
źródło
Nadal nie do końca to rozumiem. Twój przykład pokazuje, że w tym przypadku korzystasz ze specjalizacji Club innej, ale nigdy nie możesz utworzyć jednej i drugiej. Słowo templatekluczowe w tym miejscu jest nadal niepotrzebne IMHO, ponieważ to, które Szostanie wybrane, zależy od tego, która Cinstancja. Bez templatesłowa kluczowego można utworzyć jedno i drugie, a zachowanie f byłoby inne dla każdego wystąpienia.
Martin
2
@Martin chodzi o to, że każdy token powinien mieć tylko jedną rolę składniową w pliku źródłowym. Na przykład token <nie jest operatorem porównania w jednej instancji szablonu i nawiasiem kątowym otwarcia w innej instancji. Ma to na celu zapewnienie, że kompilatory mogą przetwarzać szablony na AST (z symbolami zastępczymi dla typów szablonów).
ecatmur
To ma sens. Dzięki!
Martin
7

funmoże, ale nie musi, być funkcją szablonu (lub może w ogóle nie istnieć) w zależności od parametru szablonu class C.

To dlatego, że możesz się specjalizować S(bez specjalizacji C):

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

Ponieważ kompilator chce wiedzieć, czy funjest szablonem, czy nie przy pierwszym spojrzeniu class C(przed podstawieniem parametru szablonu), templatejest wymagany.

HolyBlackCat
źródło
1
umysł ... zdmuchnięty ...
bolov
Następstwem tego jest zatem to, czy te nowe definicje Sbędą kiedykolwiek dostępne f. Jeśli nie mogą, ograniczenie to nie ma sensu, ponieważ i ftak nie będzie w stanie ich zobaczyć.
Martin
@Martin Zarówno GCC, Clang , jak i MSVC fgo znajdują.
HolyBlackCat
@bolov Tak, możesz specjalizować się w wielu różnych rzeczach .
HolyBlackCat