为什么C++标准指定模板中的非限定名称是非依赖的?
例如
template<typename T>
class Base
{
public:
T x;
};
template<typename T>
class C : public Base<T>
{
public:
bool m() { return x == 0; } // Error: undeclared identifier 'x'
};
Run Code Online (Sandbox Code Playgroud)
引用一个关于如何克服限制的SO 问题的接受答案:
该标准指定模板中的非限定名称是非依赖的,并且必须在定义模板时查找.当时未知依赖基类的定义(可能存在基类模板的特化),因此无法解析非限定名称.
但是,引用和其他答案没有说明为什么这是标准指定的内容.这种限制的理由是什么?
T.C*_*.C. 16
这不是标准实际所说的.它实际上说的是在非限定名称查找期间不检查依赖基类.非正式名称当然可以依赖于正确的上下文 - 这就是模板中ADL的工作方式:给定模板类型参数T,fooin foo(T())是依赖名称.
无论如何,它无法进行这种查找的原因很简单:在模板定义时,您不知道依赖基类会是什么样的,这要归功于以后可能出现的特殊化,所以你可以做任何有意义的查找.具有依赖基类的模板中的每个拼写错误的标识符在实例化之前都无法诊断,如果有的话.并且因为每个非限定标识符可能来自依赖基类,所以你需要一些方法来回答"这是一个类型吗?" 并且"这是一个模板吗?",因为这些问题的答案会影响解析.这意味着像可怕的typename和template关键字,但在更多的地方.
我认为这是一致的问题.考虑一下这个有点修改的例子:
头文件:
template<typename T>
class Base
{
public:
T x;
};
extern int x;
template<typename T>
class C : public Base<T>
{
public:
bool m() { return x == 0; }
};
Run Code Online (Sandbox Code Playgroud)
和源文件:
template<>
class Base<int> // but could be anything
{
public:
// no x here
};
int main()
{
C<char> c1;
c1.m(); // may select ::x or Base<char>::x
C<int> c2;
c2.m(); // has only one choice: ::x
return(0);
}
Run Code Online (Sandbox Code Playgroud)
标准中的措辞保证编译器将错误输出或选择它在模板定义时看到的任何符号.如果编译器将名称解析推迟到模板实例化,则可以选择此时可见的不同对象,这可能会让开发人员感到惊讶.
如果开发者想要访问依赖名称,则需要明确说明,然后他就不应该采取措施.
还请注意,如果x在模板定义时不可用,则以下代码将(意外地)中断一个定义规则:
one.cpp
#include <template_header>
namespace {
int x;
}
void f1()
{
C<int> c1;
}
Run Code Online (Sandbox Code Playgroud)
two.cpp
#include <template_header>
namespace {
char x;
}
void f2()
{
C<int> c2;
}
Run Code Online (Sandbox Code Playgroud)
可以预期c1和c2变量具有相同的类型,并且可以例如安全地传递两个函数获取C<int> const &参数,但基本上这两个变量具有相同的类型名称但实现不同.