c++ - 如何编写具有超过 2 层继承的奇怪重复模板?

标签 c++ templates crtp

我读过的关于 Curiously Recurring Template Pattern 的所有 Material 似乎都是一层继承,即 BaseDerived : Base<Derived> .如果我想更进一步怎么办?

#include <iostream>
using std::cout;


template<typename LowestDerivedClass> class A {
public:
    LowestDerivedClass& get() { return *static_cast<LowestDerivedClass*>(this); }
    void print() { cout << "A\n"; }
};
template<typename LowestDerivedClass> class B : public A<LowestDerivedClass> {
    public: void print() { cout << "B\n"; }
};
class C : public B<C> {
    public: void print() { cout << "C\n"; }
};

int main()
{
    C c;
    c.get().print();

//  B b;             // Intentionally bad syntax, 
//  b.get().print(); // to demonstrate what I'm trying to accomplish

    return 0;
}

我怎样才能重写这段代码以编译不出错并显示

C
B

使用 c.get().print() 和 b.get().print() ?

动机:假设我有三个类(class),

class GuiElement { /* ... */ };
class Window : public GuiElement { /* ... */ };
class AlertBox : public Window { /* ... */ };

每个类在其构造函数中接受 6 个左右的参数,其中许多是可选的并且具有合理的默认值。致avoid the tedium of optional parameters , 最好的 solution是使用Named Parameter Idiom .

这个习惯用法的一个基本问题是参数类的函数必须返回它们被调用的对象,但一些参数被提供给 GuiElement,一些给 Window,还有一些给 AlertBox。你需要一种写法:

AlertBox box = AlertBoxOptions()
    .GuiElementParameter(1)
    .WindowParameter(2)
    .AlertBoxParameter(3)
    .create();

但这可能会失败,因为例如 GuiElementParameter(int) 可能返回 GuiElementOptions&,它没有 WindowParameter(int) 函数。

这是asked之前,解决方案似乎是好奇重复模板模式的一些味道。我使用的特殊 flavor 是here .

不过,每次我创建一个新的 Gui 元素时都要编写很多代码。我一直在寻找简化它的方法。复杂性的主要原因是我使用 CRTP 来解决命名参数惯用语问题,但我有三层而不是两层(GuiElement、Window 和 AlertBox)和我的 current workaround。我的类(class)数量翻了两番。 (!) 例如,Window、WindowOptions、WindowBuilderT 和 WindowBuilder。

这让我想到了我的问题,我本质上是在寻找一种更优雅的方式来在长继承链上使用 CRTP,例如 GuiElement、Window 和 Alertbox。

最佳答案

我并不完全清楚您希望完成什么,但这与您的要求非常接近。

template <typename LowestDerivedClass> class A {
public:
  LowestDerivedClass &get() {
    return *static_cast<LowestDerivedClass *>(this); 
  }
  void print() {
    cout << "A"; 
  }
};

template <typename LowestDerivedClass>
class Bbase : public A<LowestDerivedClass> {
public:
  void print() {
    cout << "B";
    this->A<LowestDerivedClass>::print();
  }
};

class B : public Bbase<B> {};

class C : public Bbase<C> {
public:
  void print() {
    cout << "C";
    this->Bbase<C>::print();
  }
};

int main() {
  C c;
  c.print();
  cout << endl;
  B b;
  b.print();
  cout << endl;
}

我更改了输出以更好地说明继承。在您的原始代码中,您不能假装 B不是模板 [您希望的最好结果是 B<> ],所以这样的事情可能是最不笨拙的处理方式。


从您的其他答案来看,(2)是不可能的。如果函数的参数足以推断它们,则可以省略函数的模板参数,但是对于类,您必须提供一些东西。 (1) 可以做,但是很别扭。撇开所有不同的层:

template<typename T> struct DefaultTag { typedef T type; };
template<typename Derived = void>
class B : public A<Derived> { /* what B should do when inherited from */ };
template<>
class B<void> : public A<DefaultTag<B<void> > > { /* what B should do otherwise */ };

你必须在每个级别做类似的事情。就像我说的,尴尬。你不能简单地说typename Derived = DefaultTag<B> >或类似的东西,因为 B还不存在。

关于c++ - 如何编写具有超过 2 层继承的奇怪重复模板?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2821012/

相关文章:

c++ - 递归模板成语如何避免基类是子类的 friend

c++ - std::map 的用途是什么?

c++ - 什么时候实例化 constexpr 函数模板?

c++ - 如何使用枚举定义容器模板

c++ - 每个花哨的指针都应该是一个迭代器吗?

c++ - CRTP派生类貌似不知道继承类型

c++ - 如何在mac上使用cmake链接OpenGL相关库?

c++ - 如何实现具有多个开关的工厂?

c++ - 我是否需要包含 "Windows.h"才能隐式使用 Windows API/与 Windows API 通信?

c++ - 多级嵌套模板。我如何让它工作?