Clang 是否正确拒绝仅通过专业化定义 class 模板的嵌套 class 的代码?
Is Clang correct to reject code in which the nested class of a class template is defined only via specializations?
给定以下 class 模板:
template<typename T>
struct Outer
{
struct Inner;
auto f(Inner) -> void;
};
我们为Outer
的每个特化分别定义Inner
:
template<>
struct Outer<int>::Inner {};
template<>
struct Outer<double>::Inner {};
然后为Outer
的所有特化定义一次成员函数f
:
auto Outer<T>::f(Inner) -> void
{
}
但是 Clang (9.0.0) 抱怨:
error: variable has incomplete type 'Outer::Inner'
auto Outer<T>::f(Inner) -> void
^
我们可以通过为 Outer
的所有其他特化提供 Inner
的定义来避免编译器错误:
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 错误还是三者中唯一一致的实现?
我相信 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 A name is a member of the current instantiation if it is
- An unqualified name that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent
base class thereof. [ Note: This can only occur when looking up a name
in a scope enclosed by the definition of a class template. — end
note ]
- ...
A name is a dependent member of the current instantiation if it is
a member of the current instantiation that, when looked up, refers to
at least one member of a class that is the current instantiation.
9 A type is dependent if it is
- ...
- a member of an unknown specialization,
- a nested class or enumeration that is a dependent member of the current instantiation,
- ...
因此第 9 段中的第一个项目符号涵盖了案例 typename T::Inner
。也就是依赖型。
与此同时,您的案例已包含在第二个项目符号中。 Outer::Inner
是在 Outer
的当前实例中找到的名称,而且它在 Outer
本身内部找到,而不是在基 class 中找到。这使它成为当前实例化的依赖成员。此名称指的是嵌套的 class。这意味着第二个项目符号中的所有条件都适用,因此 Outer::Inner
也成为依赖类型!
由于我们在这两种情况下都有自己的依赖类型,编译器应该将它们视为依赖类型。我的结论是GCC和MSVC是对的。
给定以下 class 模板:
template<typename T>
struct Outer
{
struct Inner;
auto f(Inner) -> void;
};
我们为Outer
的每个特化分别定义Inner
:
template<>
struct Outer<int>::Inner {};
template<>
struct Outer<double>::Inner {};
然后为Outer
的所有特化定义一次成员函数f
:
auto Outer<T>::f(Inner) -> void
{
}
但是 Clang (9.0.0) 抱怨:
error: variable has incomplete type 'Outer::Inner'
auto Outer<T>::f(Inner) -> void
^
我们可以通过为 Outer
的所有其他特化提供 Inner
的定义来避免编译器错误:
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 错误还是三者中唯一一致的实现?
我相信 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 A name is a member of the current instantiation if it is
- An unqualified name that, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof. [ Note: This can only occur when looking up a name in a scope enclosed by the definition of a class template. — end note ]
- ...
A name is a dependent member of the current instantiation if it is a member of the current instantiation that, when looked up, refers to at least one member of a class that is the current instantiation.
9 A type is dependent if it is
- ...
- a member of an unknown specialization,
- a nested class or enumeration that is a dependent member of the current instantiation,
- ...
因此第 9 段中的第一个项目符号涵盖了案例 typename T::Inner
。也就是依赖型。
与此同时,您的案例已包含在第二个项目符号中。 Outer::Inner
是在 Outer
的当前实例中找到的名称,而且它在 Outer
本身内部找到,而不是在基 class 中找到。这使它成为当前实例化的依赖成员。此名称指的是嵌套的 class。这意味着第二个项目符号中的所有条件都适用,因此 Outer::Inner
也成为依赖类型!
由于我们在这两种情况下都有自己的依赖类型,编译器应该将它们视为依赖类型。我的结论是GCC和MSVC是对的。