为什么当一个模板类继承自另一个模板类时,需要重新指定 typedef 并限定函数调用?



当一个模板类继承自另一个模板类时,基类中的typedefs必须再次重新定义(即它们不会自动继承),并且基类中的函数调用需要限定。为什么?这不是已经明确了吗?

因此,如果我有 20 个模板类,所有模板类都定义相同的 typedefs,我无法引入包含这些定义的基类并从中继承,因为我必须在每个类中重新定义 typedef,这违背了目的。这使得源代码变得如此不必要地冗长。

我可以看到这个问题已经讨论过了,但我不明白评论

C++名称查找规则指定,仅当名称依赖于模板参数(如果它是"依赖名称")时,才在模板化基类中搜索该名称。如果名称不依赖于模板参数,则不会在此处搜索该名称。

这是什么原因呢?这对我来说毫无意义。

也许,以下代码片段可以更好地说明我的问题:

#include <iostream>
template <unsigned N, typename T>
struct A
{
typedef T U;
static void foo(T x) { std::cout << N + x << "n"; }
};
template <unsigned N, typename T>
struct B : public A<N, T>
{
// Why do I have to redeclare U? Isn't is unambiguous already?
typedef typename A<N, T>::U U;
//  why do I have to specify B::? Isn't it unambiguous already?
static void zoo(U x) { B::foo(x); }  
};
int main()
{
B<2,int>().zoo(3);
B<2,double>().zoo(3.5);
return 0;
}

这是因为两阶段查找。从这里引用:

模板
  1. 定义时间:当模板最初被解析时,早在它被实例化之前,编译器就会解析模板和 查找任何"非依赖"名称。在以下情况下,名称为"非依赖" 名称查找的结果不依赖于任何模板参数,并且 因此,从一个模板实例化到另一个模板实例化将是相同的。
  2. 模板
  3. 实例化时间:当模板实例化时,编译器会查找任何"依赖"名称,现在它已经有了完整的集合 要执行查找的模板参数。此查找的结果 可以(并且经常这样做!)从一个模板实例化到另一个模板实例化。

因此,在初始解析期间,编译器认为U是非依赖名称并尝试查找它,但它找不到任何内容,因为它不允许查看依赖基类。但是U如果依赖名称,则编译器将在实例化阶段查找它,并在基类中找到它。

顺便说一句:VS很容易编译这个,但他们最近增加了两阶段查找的可能性。

根本原因是类可以专业化:

template<class T>
struct A {};
template<class T>
struct B : A<T> {
typedef typename A<T>::referent target;
void niceName() {A<T>::uglyName();}
};
template<class T>
struct A<T*> {
typedef T referent;
void uglyName();
};
B<int*>::target i;  // OK: int

当然,相反的情况(主模板定义了有用的东西,我们只是担心专业化可能会改变或删除它们)更常见,这使得规则看起来很武断。

最新更新