Чи правильно Кланг відхиляти код, у якому вкладений клас шаблону класу визначається лише за допомогою спеціалізації?


17

Дано такий шаблон класу:

template<typename T>
struct Outer
{
    struct Inner;

    auto f(Inner) -> void;
};

ми визначаємо Innerокремо для кожної спеціалізації Outer:

template<>
struct Outer<int>::Inner {};

template<>
struct Outer<double>::Inner {};

а потім fодин раз визначити функцію члена для всіх спеціалізацій Outer:

auto Outer<T>::f(Inner) -> void
{

}

але Кланг (9.0.0) скаржиться:

error: variable has incomplete type 'Outer::Inner'

auto Outer<T>::f(Inner) -> void

                      ^

Ми можемо уникнути помилки компілятора, надавши також визначення Innerдля всіх інших спеціалізацій Outer:

template<typename T>
struct Outer<T>::Inner {};

або fокремо визначившись для кожної спеціалізації:

template<>
auto Outer<int>::f(Inner) -> void
{

}

template<>
auto Outer<double>::f(Inner) -> void
{

}

І GCC, і MSVC приймають початковий код, який ставить питання; це помилка Clang чи це єдина відповідна реалізація з трьох?

Спробуйте в Провіднику компілятора


Спеціалізації Внутрішньої не мають значення, видалення їх не змінює результату компіляції.
н. 'займенники' м.

@ n.'pronouns'm. Я не впевнений, що ти маєш на увазі. І додавання визначення Innerдля всіх інших спеціалізацій, і окремо визначення fдля кожної спеціалізації вирішують помилку компіляції.
викладено

Давайте прочитаємо його ще раз: видалення їх не змінює результату компіляції . Не додавання, видалення. gcc clang
n. 'займенники' м.

@ n.'pronouns'm. Я бачу, що ви маєте на увазі зараз, але це все ще дивний коментар. Суть мого питання полягала в тому, що Innerвін повідомляється як неповний тип, незважаючи на визначення для кожної спеціалізації, Outerщо надається. Очевидно Inner, що (правильно) буде неповним типом, якщо ви видалите його визначення.
викладено

"Очевидно, що Внутрішня буде (правильно) незавершеним типом, якщо ви видалите її визначення (і)." Ні, що "взагалі не ckear. Спеціалізація - це абсолютно окремий шаблон, і він взагалі не впливає на основний шаблон.
п. 'займенники' м.

Відповіді:


4

Я вважаю, що Кланг неправильно відхиляє ваш код. Ми повинні запитати себе, як порівнюються декларація та визначення вашої функції

auto f(typename T::Inner) -> void;

// ...

template<typename T>
auto Outer<T>::f(typename T::Inner) -> void
{ }

У цьому прикладі T::Inner очевидно, залежить від типу. Таким чином, Кланг може не вважати, що він неповний до моменту. Чи справедливо те саме у вашому прикладі? Я б сказав так. Бо ми маємо це у стандарті:

[temp.dep.type]

5 Ім'я є членом поточної інстанції якщо воно є

  • Некваліфіковане ім'я, яке при погляді вказує на щонайменше одного члена класу, який є поточною інстанцією або незалежним базовим класом. [Примітка. Це може статися лише під час пошуку імені в області, що додається до визначення шаблону класу. - кінцева примітка]
  • ...

Ім'я є залежним членом поточної інстанції, якщо воно є членом поточної інстанції, який при погляді звертається на щонайменше одного члена класу, який є поточною інстанцією.

9 Тип залежить, якщо він є

  • ...
  • член невідомої спеціалізації,
  • вкладений клас або перерахунок, який є залежним членом поточної інстанції,
  • ...

Тож перша куля в пункті 9 охоплює справу typename T::Inner . Це залежний тип.

Тим часом вашу справу охоплює друга куля. Outer::Innerце ім'я, яке зустрічається в поточній інстанції Outer, до того ж воно знайдено всередині Outerсебе, а не в базовому класі. Це робить його залежним членом поточної інстанції. Ця назва відноситься до вкладеного класу. Що означає, що всі умови другої кулі застосовуються, таким чиномOuter::Inner і залежний тип!

Оскільки в обох випадках ми маємо залежний тип, компілятори повинні трактувати їх однаково як залежні типи. Мій висновок: GCC та MSVC мають рацію.


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