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错误,还是这三个中唯一的一致实现?
我认为 Clang 拒绝你的代码是错误的。我们必须问自己,你的函数声明和定义与
\n\nauto f(typename T::Inner) -> void;\n\n// ...\n\ntemplate<typename T>\nauto Outer<T>::f(typename T::Inner) -> void\n{ }\nRun Code Online (Sandbox Code Playgroud)\n\n在这个例子中,T::Inner显然是一个依赖类型。因此,在实例化之前,Clang 可能不会认为它是不完整的。在你的例子中也是如此吗?我会这么说。因为我们的标准中有这样的内容:
\n\n\n[温度相关类型]
\n\n5名称是当前实例化的成员,如果它是
\n\n\n
\n\n- 一种非限定名称,在查找时至少引用当前实例化类或其非依赖基类的一个成员。[\xe2\x80\x89注意:只有在类模板定义所包含的范围内查找名称时,才会发生这种情况。\xe2\x80\x89\xe2\x80\x94\xe2\x80\x89end\n 注意\xe2\x80\x89]
\n- ...
\n如果某个名称是当前实例化的成员,并且在查找时引用当前实例化的类的至少一个成员,则该名称是当前实例化的从属成员。
\n\n9一个类型是相关的,如果它是
\n\n\n
\n- ...
\n- 未知专业的成员,
\n- 作为当前实例化的依赖成员的嵌套类或枚举,
\n- ...
\n
因此,第 9 段的第一个项目符号涵盖了这个案例typename T::Inner。那是依赖型。
与此同时,你的案子已被第二颗子弹覆盖。Outer::Inner是在 的当前实例化中找到的名称Outer,而且它是在Outer其自身内部找到的,而不是在基类中找到的。这使得它成为当前实例化的依赖成员。该名称指的是嵌套类。这意味着第二个项目符号中的所有条件都适用,从而Outer::Inner也形成了依赖类型!
由于在这两种情况下我们都有自己的依赖类型,因此编译器应该将它们同等地视为依赖类型。我的结论是 GCC 和 MSVC 是正确的。
\n