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.