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
{

}

但是Clang(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的专业化是无关紧要的,删除它们不会改变编译结果。
n。代词

@n。'代词'。我不确定你是什么意思。这两种添加的定义Inner为所有其他专长,并定义f为每个单独的专业化解决编译错误。

让我们再读一遍:删除它们不会更改编译结果。不添加,删除。gcc clang
n。代词

@n。'代词'。我明白您的意思了,但这仍然是一个奇怪的评论。我的问题的重点是,Inner尽管提供了每种专业的定义,但仍被报告为不完整的类型OuterInner如果删除定义,显然(正确)将是不完整的类型。
上贴

“显然心灵意志(正确的),如果你删除它的定义(S)是一个不完整的类型。”不说“不是ckear时所用的专业化是一个完全独立的模板,它根本不会影响主模板。
ñ。“代词”米。

Answers:


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是正确的。


错误报告。谢谢。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.