为什么C ++标准指定模板中的非限定名称是非依赖的?
e.g。
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'
};
从接受的SO question答案中引用如何克服限制:
该标准指定模板中的非限定名称 非依赖性,必须在定义模板时查找。该 当时未知的依赖基类的定义 (基类模板的特化可能存在)因此不合格 名称无法解析。
但是,引用和其他答案没有指定为什么这是标准指定的内容。这种限制的理由是什么?
答案 0 :(得分:16)
这并不是标准实际所说的。它实际上说的是在非限定名称查找期间不检查依赖基类。非正式名称当然可以依赖于正确的上下文 - 模板中的ADL如何工作:给定模板类型参数T
,foo
中的foo(T())
是依赖项名。
无论如何,它不能进行这种查找的原因很简单:在模板定义时,您不知道依赖基类会是什么样的,这要归功于可能出现的特殊化之后,您无法进行任何有意义的查找。具有依赖基类的模板中的每个拼写错误的标识符都不能被诊断,直到它被实例化为止,如果有的话。并且由于每个不合格的标识符可能来自依赖基类,因此您需要某种方式来回答这是一种类型?&#34;和&#34;这是一个模板吗?&#34;,因为这些问题的答案会影响解析。这意味着可怕的typename
和template
关键字,但在更多的地方。
答案 1 :(得分:7)
我认为这是一致的问题。考虑一下,有点修改的例子:
头文件:
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; }
};
和源文件:
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);
}
标准中的措辞保证编译器将错误输出或选择它在模板定义时看到的任何符号。如果编译器将名称解析推迟到模板实例化,那么它可能会选择此时可见的不同对象,这可能会让开发人员感到惊讶。
如果开发者想要访问依赖名称,他必须明确说明这一点,然后他就不应该采取措施。
另请注意,如果x在模板定义时无法使用,则以下代码会中断(意外)一个定义规则:
one.cpp
#include <template_header>
namespace {
int x;
}
void f1()
{
C<int> c1;
}
two.cpp
#include <template_header>
namespace {
char x;
}
void f2()
{
C<int> c2;
}
可以预期c1和c2变量属于同一类型,并且例如可以安全地传递两个带有C<int> const &
参数的函数,但基本上这两个变量具有相同的类型名称,但实现方式不同。 / p>
答案 2 :(得分:1)
在Visual Studio 2015中是否符合标准 - 这根本不是问题。此外,尽管Tomek发表了评论,但编译器采用了正确的x
static int x = 42;
template<typename T>
class Base {
public:
T x;
Base() { x = 43; }
};
template<>
class Base<int> { };
template<typename T>
class C :public Base<T>
{
public:
int m() { return x; }
};
void main() {
C<int> cint;
C<char> cchar;
std::cout << "cint: " << cint.m() << std::endl;
std::cout << "cchar: " << cchar.m() << std::endl;
}
// Output is:
// cint: 42
// cchar: 43