C++ 具有依赖类型的CRTP,类型名称查找
关于如何在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
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
部分已经属于类定义(想想你将如何向前声明一个类)。与函数一样,只检查声明,但延迟检查定义部分。其他人能证实这一点吗?还是我走错了路?