是否可以使用CRTP访问C++中的子类型?
我有这个玩具示例,
template <typename TChild>
struct Base {
template <typename T>
using Foo = typename TChild::template B<T>;
};
struct Child : Base<Child> {
template <typename T>
using B = T;
};
using Bar = Child::Foo<int>;
无法编译。目的是我有一个父类,它提供基于子类成员的类型计算。子类是通过 CRTP 提供的。然而线
using Foo = typename TChild::template B<T>;
using Foo = typename TChild::template B<T>;
无法编译:
<source>: In instantiation of 'struct Base<Child>':
<source>:16:16: required from here
<source>:13:11: error: invalid use of incomplete type 'struct Child'
13 | using Foo = typename TChild::template B<T>;
| ^~~
<source>:16:8: note: forward declaration of 'struct Child'
16 | struct Child : Base<Child> {
| ^~~~~
我期待这样的构造起作用是天真吗?
https://godbolt.org/z/5Prb84 上的失败代码
回答
让我发布另一种方法来做到这一点:
template<typename TChild, class T>
struct GetB {
using Type = typename TChild::template B<T>;
};
template<typename TChild>
struct Base {
template<typename T>
using Foo = typename GetB<TChild, T>::Type;
};
struct Child : Base<Child> {
template<typename T>
using B = T;
};
我没有语言律师类型的解释为什么这有效,但它应该与具有额外的间接级别有关。当编译器看到
它可以(并且将会)在此时检查和抱怨使用了不完整的类型。然而,当我们将 access 包装B<T>成一个函数或结构体时,
using Foo = typename GetB<TChild, T>::Type;
那么此时我们没有访问内部TChild,我们只是使用它的名称,这很好。
回答
CRTP 的问题是派生类在 CRTP 定义中不完整,因此您不能使用其using.
在
template <typename T>
using Foo = typename TChild::template B<T>;
- 完整的类型
TChild将需要因::。 TChild是不依赖模板的T,所以第一遍检查应该做(但没有)
您可能会使用外部特征来处理这种情况
template <typename C, typename T>
struct Traits_For_Base
{
using type = typename C::template B<T>;
};
template <typename TChild>
struct Base {
template <typename T>
using Foo = typename Traits_For_Base<TChild, T>::type;
};
Traits_For_Base<TChild, T>是相关的T,所以没什么可用于第一遍检查做。并且,通过第二次通过检查(依赖于T),Child将完成。
演示
或者您可以更改别名以使其类型依赖于类的模板参数Base:
template <typename TChild>
struct Base {
template <typename T,
typename C = TChild,
std::enable_if_t<std::is_same_v<C, TChild>, int> = 0> // To avoid hijack
using Foo = typename C::template B<T>;
};
C是依赖模板的,所以不能在第一阶段进行检查。
演示