Clang是否正确拒绝仅通过专门化定义类模板的嵌套类的代码?

inv*_*xed 16 c++ templates template-specialization language-lawyer incomplete-type

给定以下类模板:

template<typename T>
struct Outer
{
    struct Inner;

    auto f(Inner) -> void;
};
Run Code Online (Sandbox Code Playgroud)

我们Inner为以下每种专业分别定义Outer

template<>
struct Outer<int>::Inner {};

template<>
struct Outer<double>::Inner {};
Run Code Online (Sandbox Code Playgroud)

然后f为以下所有专业定义一次成员函数Outer

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

}
Run Code Online (Sandbox Code Playgroud)

但是Clang(9.0.0)抱怨:

error: variable has incomplete type 'Outer::Inner'

auto Outer<T>::f(Inner) -> void

                      ^
Run Code Online (Sandbox Code Playgroud)

我们还可以通过提供Inner以下所有其他专业的定义来规避编译器错误Outer

template<typename T>
struct Outer<T>::Inner {};
Run Code Online (Sandbox Code Playgroud)

或通过f为每个专业分别定义:

template<>
auto Outer<int>::f(Inner) -> void
{

}

template<>
auto Outer<double>::f(Inner) -> void
{

}
Run Code Online (Sandbox Code Playgroud)

GCC和MSVC都接受初始代码,这就是问题所在。这是Clang错误,还是这三个中唯一的一致实现?

尝试编译器资源管理器

Sto*_*ica 4

我认为 Clang 拒绝你的代码是错误的。我们必须问自己,你的函数声明和定义与

\n\n
auto f(typename T::Inner) -> void;\n\n// ...\n\ntemplate<typename T>\nauto Outer<T>::f(typename T::Inner) -> void\n{ }\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这个例子中,T::Inner显然是一个依赖类型。因此,在实例化之前,Clang 可能不会认为它是不完整的。在你的例子中也是如此吗?我会这么说。因为我们的标准中有这样的内容:

\n\n
\n

[温度相关类型]

\n\n

5名称是当前实例化的成员,如果它是

\n\n
    \n
  • 一种非限定名称,在查找时至少引用当前实例化类或其非依赖基类的一个成员。[\xe2\x80\x89注意:只有在类模板定义所包含的范围内查找名称时,才会发生这种情况。\xe2\x80\x89\xe2\x80\x94\xe2\x80\x89end\n 注意\xe2\x80\x89]
  • \n
  • ...
  • \n
\n\n

如果某个名称是当前实例化的成员,并且在查找时引用当前实例化的类的至少一个成员,则该名称是当前实例化的从属成员。

\n\n

9一个类型是相关的,如果它是

\n\n
    \n
  • ...
  • \n
  • 未知专业的成员,
  • \n
  • 作为当前实例化的依赖成员的嵌套类或枚举,
  • \n
  • ...
  • \n
\n
\n\n

因此,第 9 段的第一个项目符号涵盖了这个案例typename T::Inner。那是依赖型。

\n\n

与此同时,你的案子已被第二颗子弹覆盖。Outer::Inner是在 的当前实例化中找到的名称Outer,而且它是在Outer其自身内部找到的,而不是在基类中找到的。这使得它成为当前实例化的依赖成员。该名称指的是嵌套类。这意味着第二个项目符号中的所有条件都适用,从而Outer::Inner也形成了依赖类型!

\n\n

由于在这两种情况下我们都有自己的依赖类型,因此编译器应该将它们同等地视为依赖类型。我的结论是 GCC 和 MSVC 是正确的。

\n