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

Why when a template class inherits from another template class, typedefs need to be respecified and functions calls qualified?

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

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

我可以看到这个question中已经讨论过了,但是我不明白评论

The C++ name lookup rules specify that a name is only searched in a templated base classes if it depends on a template parameter (if it is a "dependent name"). If a name does not depend on a template parameter it isn't searched there.

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

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

#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;
}

这是因为两阶段查找。引自 here:

  1. Template definition time: when the template is initially parsed, long before it is instantiated, the compiler parses the template and looks up any "non-dependent" names. A name is "non-dependent" if the results of name lookup do not depend on any template parameters, and therefore will be the same from one template instantiation to another.
  2. Template instantiation time: when the template is instantiated, the compiler looks up any "dependent" names, now that it has the full set of template arguments to perform lookup. The results of this lookup can (and often do!) vary from one template instantiation to another.

因此,在初始解析期间,编译器认为 U 是非依赖名称并尝试查找它但找不到任何内容,因为它不允许查看 依赖基础class。但是,如果 U 是从属名称,那么编译器会在实例化阶段查找它,并会在基础 class.

中找到它

顺便说一句: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

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