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


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.