是否可以使用 CRTP 访问 C++ 中的子类型?

bra*_*ing 18 c++ crtp template-meta-programming c++14

我有这个玩具示例,

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>;
Run Code Online (Sandbox Code Playgroud)

无法编译。目的是我有一个父类,它提供基于子类成员的类型计算。子类是通过 CRTP 提供的。然而线

using Foo = typename TChild::template B<T>;
Run Code Online (Sandbox Code Playgroud)

无法编译:

<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> {
      |        ^~~~~
Run Code Online (Sandbox Code Playgroud)

我期待这样的构造起作用是天真吗?

https://godbolt.org/z/5Prb84 上的失败代码

Evg*_*Evg 10

让我发布另一种方法来做到这一点:

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;
};
Run Code Online (Sandbox Code Playgroud)

我没有语言律师类型的解释为什么这有效,但它应该与具有额外的间接级别有关。当编译器看到

using Foo = typename TChild::template B<T>;
Run Code Online (Sandbox Code Playgroud)

它可以(并且将会)在此时检查和抱怨使用了不完整的类型。然而,当我们将 access 包装B<T>成一个函数或结构体时,

using Foo = typename GetB<TChild, T>::Type;
Run Code Online (Sandbox Code Playgroud)

那么此时我们没有访问内部TChild,我们只是使用它的名称,这很好。


Jar*_*d42 7

CRTP 的问题是派生类在 CRTP 定义中不完整,因此您不能使用其using.

template <typename T>
using Foo = typename TChild::template B<T>;
Run Code Online (Sandbox Code Playgroud)
  • 完整的类型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;
};
Run Code Online (Sandbox Code Playgroud)

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>;
};
Run Code Online (Sandbox Code Playgroud)

C依赖模板的,所以不能在第一阶段进行检查。

演示