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

C++ 具有依赖类型的CRTP,类型名称查找,c++,crtp,dependent-type,C++,Crtp,Dependent Type,关于如何在CRTP中不使用派生类中的嵌套类型,有很多讨论。但是,以下某些情况实际上效果良好: 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

关于如何在CRTP中不使用派生类中的嵌套类型,有很多讨论。但是,以下某些情况实际上效果良好:

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;
};
模板
阶级基础{
公众:
int value=T().value;
};
模板
B类{
公众:
//案例1:可以,只要C类没有实例化;
C类:公共T::I{};
//案例2:坏,C类实例化;
//C类:公共T::I{};
//C C;
//案例三:坏,
//typedef typename T::I TI;
//案例4:糟糕,类似地,甚至T::I也被用作模板参数
//typedef-Base-BaseTI;
//案例5:如果用作函数参数类型,则制作一个普通模板
模板>
自动获取(rn){返回n;}
};
D类:公共B类{
公众:
第一类{
公众:
int值;
};
I I;
};
我认为2,3,4是如何失败的是由于编译器的2阶段名称查找;我还可以推断案例5是可以的,因为只有在调用
B:::get
时,它才能得到解决。然而,我不明白为什么案例1运行良好,特别是给定的案例2没有编译


为什么1和2不同?

当编译器看到
类D:public B
时,它会尝试实例化
B
。此时,
D
仍然是一个不完整的类型,
I
还不清楚。然而,由于“延迟实例化”,嵌套类直到第一次使用才被实例化。由此:

…其成员定义的实例化将推迟到实际使用时进行。这不仅适用于成员函数,还适用于静态数据成员和嵌套类


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

当您的编译器看到
类D:public B
时,它尝试实例化
B
。此时,
D
仍然是一个不完整的类型,
I
还不清楚。然而,由于“延迟实例化”,嵌套类直到第一次使用才被实例化。由此:

…其成员定义的实例化将推迟到实际使用时进行。这不仅适用于成员函数,还适用于静态数据成员和嵌套类


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

尝试了静态数据案例。但是,
const static int i=T::i().value
常量静态类型名T::I ti仍然给出相同的错误'error:D'中没有名为'I'的成员';看起来,即使静态数据被实例化;它的类型被查找了吗?我想我现在可能有了感觉:它只指被延迟的静态
变量
实例化,而不是它的类型(因此类型查找);类似地,对于成员函数,它的定义没有被检查(这样T::I就可以出现在函数定义体中,而不是出现在它的签名中)。但是,我不完全理解,为什么对于嵌套类,
类C:public T::I
,它声明
C
是从
T::I
派生的,
T::I
未被检查/查找?@dragonxlwang我也不是这方面的专家,但我认为
public T::I
部分已经属于类定义(想想你将如何向前声明一个类)。与函数一样,只检查声明,但延迟检查定义部分。其他人能证实这一点吗?还是我走错了方向?试过静态数据案例。但是,
const static int i=T::i().value
常量静态类型名T::I ti仍然给出相同的错误'error:D'中没有名为'I'的成员';看起来,即使静态数据被实例化;它的类型被查找了吗?我想我现在可能有了感觉:它只指被延迟的静态
变量
实例化,而不是它的类型(因此类型查找);类似地,对于成员函数,它的定义没有被检查(这样T::I就可以出现在函数定义体中,而不是出现在它的签名中)。但是,我不完全理解,为什么对于嵌套类,
类C:public T::I
,它声明
C
是从
T::I
派生的,
T::I
未被检查/查找?@dragonxlwang我也不是这方面的专家,但我认为
public T::I
部分已经属于类定义(想想你将如何向前声明一个类)。与函数一样,只检查声明,但延迟检查定义部分。其他人能证实这一点吗?还是我走错了路?