Правильно ли 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

invexed
источник
Специализации 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 правы.

Рассказчик - Unslander Monica
источник
Об ошибке сообщили . Спасибо.
выпуклый