具有依赖类型的 CRTP,类型名称查找

CRTP with dependent type, type name lookup

有很多关于如何在 CRTP 中不使用派生 class 中的嵌套类型的讨论。但是,下面的某些情况实际上可以正常工作:

template <class T>
class Base {
 public:
  int value = T().value;
};

template <class T>
class B {
 public:

  // case 1: ok, as long as class C does not instantiate;
  class C : public T::I {};

  // case 2: bad, class C instantiated;
  // class C : public T::I {};
  // C c;

  // case 3: bad,
  // typedef typename T::I TI;

  // case 4: bad, similarly, even T::I is used as template parameter
  // typedef Base<typename T::I> BaseTI;

  // case 5: if used as function parameter type, make a trivial template 
  template <typename R, 
            typename = std::enable_if_t<std::is_same_v<T, typename R>::type>>>
  auto get(R n) { return n; }
};

class D : public B<D> {
 public:
  class I {
   public:
    int value;
  };
  I i;
};

我认为 2、3、4 失败的原因是编译器的两阶段名称查找;我也可以推断案例 5 是可以的,因为只有当 B::<T>::get<R> 被调用时它才会得到解决。但是,我不明白为什么案例 1 运行良好,尤其是给定的案例 2 无法编译。

为什么 1 和 2 不同?

当您的编译器看到 class D : public B<D> 时,它会尝试实例化 B<D>。在这一点上,D 仍然是一个不完整的类型,I 还不知道。然而,由于“延迟实例化”,嵌套的 class 直到第一次使用时才被实例化。从这个 answer:

...instantiation of its member definitions are deferred until they are actually used. This does not only apply to member functions, but also to static data members and nested classes.

因此情况 2 失败,但情况 1 工作正常。从引用的答案中,您还可以看到 typedef 没有延迟,因此,当您声明它们时 D 是不完整的。