CRTP - 嵌套叶类

时间:2017-06-26 11:54:14

标签: c++ inner-classes forward-declaration crtp

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

#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是否应该知道inner的存在。但是,我想在inner中定义B::Inner类型的对象A,而不是在inner中提供B的定义,并在A中使用它{1}}。
  • 我知道我无法转发声明B和/或B::Inner以及无法完成此操作的原因。因此,从技术上讲,设计问题没有解决方案。但是,我正在寻找一种可行的解决方法。

我已经考虑了几种替代解决方案:

  • 可能的可行解决方案之一是不尝试在B::Inner inner中“定义”A并使用A的成员函数来提供允许修改{的功能{1}} A<B>::Inner的一部分。
  • 另一种可能的解决方案是明确定义类B::Inner innerA<B>::Inner(即不是嵌套类)。但是,我宁愿避免这种情况,因为根据设计,不期望任何不从B::Inner派生的类需要与A或从{{1}派生的类进行交互。 }}

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

2 个答案:

答案 0 :(得分:1)

标准说:

  

在类说明符的结束},类被视为完全定义的对象类型(或完整类型)。

因此,BA专门化为A<B>时,A不是完全定义的对象。因此,您无法期望能够从A的定义中访问其成员或类型或任何内容(即使您可以在{{的成员方法的定义中回调派生类) 1}},这是完全合法的,而不是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是(我说)仅在实例U时通过mvInnerA 间接使用你没有上面提到的问题。付费的代价是存在恼人的<>以及可以将自定义类型传递给mvInnerA的事实。

答案 1 :(得分:0)

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

类模板定义本身的范围没有用处。在实例化模板时,需要完全定义类型B,类似于skypjack points out,它不是。但是,您可以在不使用类模板实例化的上下文中使用它,类模板主要是A的成员函数。

虽然您不能拥有B::Inner的类型别名,但您可以拥有类型别名模板

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

A个成员函数可用于避免typename B::Inner的详细程度,而是使用Inner<B>