Дан следующий шаблон класса:
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 или единственная совместимая реализация из трех?
Inner
для всех других специализаций, и определениеf
отдельно для каждой специализации устраняет ошибку компиляции.Inner
он представляется как неполный тип, несмотря на определения для каждой специализацииOuter
. Ясно,Inner
что (правильно) будет неполный тип, если вы удалите его определения.Ответы:
Я считаю, что Clang не прав, чтобы отклонить ваш код. Мы должны спросить себя, как соотносятся ваше объявление и определение функции с
В этом примере,
T::Inner
очевидно, является зависимым типом. Таким образом, Clang может не предполагать, что он неполон до момента его создания. То же самое относится и к вашему примеру? Я бы так сказал. Ибо у нас есть это в стандарте:Таким образом, первая пуля в пункте 9 охватывает случай
typename T::Inner
. Это зависимый тип.Между тем ваш случай покрыт второй пулей.
Outer::Inner
это имя, которое встречается в текущем экземпляреOuter
, более того, оно находится внутриOuter
себя, а не в базовом классе. Это делает его зависимым членом текущего экземпляра. Это имя относится к вложенному классу. Это означает, что все условия, указанные во втором пункте, применимы, что также делаетOuter::Inner
зависимый тип!Поскольку в обоих случаях мы сами имеем зависимый тип, компиляторы должны одинаково относиться к ним как к зависимым типам. Мой вывод заключается в том, что GCC и MSVC правы.
источник