C ++ - Чому тут потрібне ключове слово "шаблон"?


9

У мене є такий код:

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

Створюючи це за допомогою gcc 9.2 та clang (9.0), я отримую помилку компіляції через те, що templateключове слово потрібно для виклику fun. Кланг показує:

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

Я не розумію, чому компілятор вважає funзалежне ім'я в контексті f, оскільки fце не сам шаблон. Якщо я заміню Cшаблону став звичайним класом, помилка усувається; однак я не бачу, чому в першу чергу повинна бути помилка, оскільки вона не залежить і Sне fзалежить від неї TC.

Як не дивно, MSVC 19.22 компілює це просто чудово.


Примітка

Перш ніж голосувати, щоб закрити як дупе Де і чому я повинен поставити ключові слова "шаблон" та "Ім'я"? будь ласка, врахуйте, що це особливий випадок, коли навіть якщо Sце дійсно залежне ім'я, в контексті цього fвоно не залежало б, якби не факт, що вони є членами поточної інстанції.


Коментарі не для розширеного обговорення; ця розмова була переміщена до чату .
Bhargav Rao

Відповіді:


10

Поміркуйте :

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)аналізуються як вираз , що містить дві операцій порівняння <і >, і це нормально для # 1 , але не для # 2.

Якщо це змінено , щоб s.template a<0>(i)потім # 2 ОК і # 1 не вдається. Таким чином, templateключове слово тут ніколи не буває зайвим.

MSVC здатний інтерпретувати вираз s.a<0>(i)обома способами в межах однієї програми. Але це не вірно згідно Стандарту; кожен вираз повинен мати лише один синтаксичний аналіз, з яким компілятор має справу.


Я досі не розумію цього повністю. Ваш приклад показує, що в цьому випадку ви або використовуєте спеціалізацію, Cабо іншу, але ніколи не можете інстанціювати обох. templateКлючове слово тут ще непотрібної ІМХО, тому що Sотримує взяв залежить від того , Cінстанцірованія. Без templateключового слова ви могли б інсталювати і те, і поведінка f відрізнялося б для кожного випадку.
Мартін

2
@Martin в тому, що кожен маркер повинен мати лише одну синтаксичну роль у вихідному файлі. Наприклад, неправильно, щоб маркер <був оператором порівняння в одному екземплярі шаблона, а дужка кута відкриття - в іншій інстанції. Це робиться для того, щоб компілятори могли розбирати шаблони AST (із заповнювачами для типів шаблонів).
екатмур

Що має сенс. Дякую!
Мартін

7

funможе або не може бути функцією шаблону (або взагалі не може існувати) залежно від параметра шаблону class C.

Це тому, що ви можете спеціалізуватися S(без спеціалізації C):

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

Оскільки компілятор хоче знати, чи funє шаблон чи ні при першому перегляді class C(перед заміною параметра шаблону) template.


1
розум ... подув ...
болов

Далі слідкує за цим, чи Sможна отримати ці нові визначення поняття f. Якщо вони не можуть, мати таке обмеження не має сенсу, оскільки fне зможе їх побачити в будь-якому випадку.
Мартін

@Martin І GCC, і Clang , і MSVC fзнаходять це.
HolyBlackCat

@bolov Так, ви можете спеціалізуватися на багатьох різних речах .
HolyBlackCat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.