具有依赖类型的 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
是不完整的。
有很多关于如何在 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
是不完整的。