【发布时间】:2020-07-06 08:06:54
【问题描述】:
考虑标准中的一个例子
template<class T> struct A {
typedef int M;
struct B {
typedef void M;
struct C;
};
};
template<class T> struct A<T>::B::C : A<T> {
M m; // OK, A<T>::M
};
评论说M指的是A<T>::M,我对此表示怀疑,因为这些规则:
在类或类模板的定义中,在非限定名称查找期间,无论是在类模板或成员的定义点还是在实例化期间,都不会检查依赖基类的范围类模板或成员的名称。
这意味着在非限定名称查找期间永远不会考虑依赖基类范围内的名称。
名称M 是一个非限定名称。因此不考虑在A<T> 中声明的M。
然后根据不合格名称查找的规则,即:
对于类 X 的成员,在成员函数体、默认参数、noexcept 说明符、非静态数据成员的大括号或等号初始化器中使用的名称,或在 X 的定义之外的类成员的定义,在成员的 declarator-id32 之后,应以下列方式之一声明:
- 如果 X 是 Y 类的嵌套类,则应是 Y 的成员,或者应是 Y 的基类的成员(此查找依次适用于 Y 的封闭类,从最里面的封闭类开始)
由于C是B的嵌套类,因此我认为查找应从B开始,然后是A,因为B范围内有一个名称M,因此应停止查找。
在 [basic.lookup.unqual] 中列出的所有情况下,将按照每个相应类别中列出的顺序搜索范围以查找声明;名称查找在找到名称声明后立即结束。如果没有找到声明,则程序格式错误。
因此,根据这些规则,A<T>::B::C 中的名称M 应指代B::M。
outcome 在这里。
GCC 同意标准所说的,但是clang 报告了一个错误并表示M 的类型是void。 clang 的结果与我的分析一致。基于这些原因,我同意clang 是对的。
所以,我想知道这是一个缺陷吗?还是我误会了什么?
【问题讨论】:
标签: c++ templates c++17 language-lawyer