Правильно ли Clang отклонять код, в котором вложенный класс шаблона класса определяется только через специализации?


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 или единственная совместимая реализация из трех?

Попробуйте в Compiler Explorer


Специализации Inner не имеют значения, удаление их не меняет результат компиляции.
— нет. местоимения м.

@ n.'pronouns'm. Я не уверен, что ты имеешь в виду. И добавление определения Innerдля всех других специализаций, и определение fотдельно для каждой специализации устраняет ошибку компиляции.
— выпуклый

Давайте прочитаем это снова: удаление их не меняет результат компиляции . Не добавление, удаление. gcc clang
— n. местоимения м.

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

«Очевидно, что Inner будет (правильно) неполным типом, если вы удалите его определения.« Нет, это вовсе не ckear. Специализация - это совершенно отдельный шаблон, и он вообще не влияет на основной шаблон.
— п. «местоимения» м.

Ответы:


4

Я считаю, что Clang не прав, чтобы отклонить ваш код. Мы должны спросить себя, как соотносятся ваше объявление и определение функции с

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

// ...

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

В этом примере, T::Innerочевидно, является зависимым типом. Таким образом, Clang может не предполагать, что он неполон до момента его создания. То же самое относится и к вашему примеру? Я бы так сказал. Ибо у нас есть это в стандарте:

[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.