CRTP - 嵌套叶类型的可见性 class

CRTP - visibility of the type of a nested leaf class

我想了解是否可以在基本 CRTP class 中使用叶 CRTP class 的嵌套 class。下面的例子演示了这个问题。

#include <iostream>

using namespace std;

template<class T>
class A
{

protected:
    T* asLeaf(void)
        {return static_cast<T*>(this);}
    T const* asLeaf(void) const
        {return static_cast<T const*>(this);}
public:

    struct Inner
    {int a = 10;};

    void dispInner(void) const
        {std::cout << asLeaf()->inner.a << std::endl;}

    // I would like to use T::Inner in this class, e.g.
    // typename T::Inner mvInnerA;
    // However, I understand that it is not possible to
    // use it in the form that is stated above. Thus, 
    // I am looking for any possible workarounds. 

};


class B: public A<B>
{
public:
    struct Inner: public A<B>::Inner
    {int b = 20;};

protected:
    friend A<B>;
    B::Inner inner;

public:
    void dispInner(void) const
        {
            A<B>::dispInner();
            std::cout << asLeaf()->inner.b << std::endl;
        }
};


int main()
{

    B b;
    b.dispInner();

    return 0;

}

编辑

我想根据收到的反馈提供一些进一步的评论:

我已经考虑过几种替代方案:

我提出的两种解决方案都可以接受。但是,我正在寻找任何可行的替代方案。

标准说:

A class is considered a completely-defined object type (or complete type) at the closing } of the class-specifier.

因此当您将 A 特化为 A<B> 时,B 不是一个完全定义的对象。因此,您不能期望能够从 A 的定义中访问其成员或类型或任何内容(即使您可以从 class 的成员方法的定义中回调派生的 class A,除了 CRTP 习语的目的之外,这是完全合法的。
换句话说,当你这样做时:

typename T::Inner mvInnerA

您无法保证 T 是一个完全定义的对象,这就是您收到错误的原因。


一些备选方案:

  1. 您可以将 mvInnerType 定义为函数而不是类型,并将其用作工厂来创建类型为 T::inner:

    的对象
    [static] auto mvInnerA() {
        return typename T::Inner{};
    }
    

    将其用作:

    auto foo = A<B>::mvInnerA();
    

    或:

    auto foo = obj.mvInnerA();
    

    正确的形式取决于你是否static
    请注意,即使无法访问其名称,您仍然可以以某种方式使用隐藏类型:

    using HiddenType = decltype(A<B>::mvInnerA());
    HiddenType foo = A<B>::mvInnerA();
    
  2. 您可以使用模板定义 mvInnerA 别名声明,如下所示:

    template<typename U = T>
    using mvInnerA = typename U::Inner;
    

    然后将其用作:

    auto foo = A<B>::mvInnerA<>{};
    

    因为类型 T 是(让我说) 仅在实例化 mvInnerA 时通过 U 间接使用,你不需要没有上面提到的问题。为此付出的代价是存在烦人的 <> 以及可以将自定义类型传递给 mvInnerA.

  3. 的事实

如何使用 CRTP 模板参数的内部类型受到严格限制。

在class 模板定义本身的范围内是没有用的。实例化模板时,需要完全定义类型 B,而 则不是。但是,您可以在未立即使用 class 模板实例化的上下文中使用它,该模板主要是 A.

的成员函数

虽然您不能为 B::Inner 设置类型别名,但您可以使用类型别名模板

template<class C>
using Inner = typename C::Inner

可以使用A的成员函数来避免typename B::Inner的冗长,而是使用Inner<B>.