【发布时间】:2011-02-18 17:52:49
【问题描述】:
我读过的关于奇怪循环模板模式的所有材料似乎都是一层继承,即Base 和Derived : 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
乙
使用 c.get().print() 和 b.get().print() ?
动机:假设我有三个班级,
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,解决方案似乎是某种奇怪重复的模板模式。我使用的特殊风味是here。
不过,每次我创建一个新的 Gui 元素时都要编写很多代码。我一直在寻找简化它的方法。复杂性的一个主要原因是我使用 CRTP 来解决 Named-Parameter-Idiom 问题,但我有三层而不是两层(GuiElement、Window 和 AlertBox),而且我的 current workaround 使我的类数量增加了四倍有。 (!) 例如,Window、WindowOptions、WindowBuilderT 和 WindowBuilder。
这让我想到了我的问题,我本质上是在寻找一种更优雅的方式来在长继承链(例如 GuiElement、Window 和 Alertbox)上使用 CRTP。
【问题讨论】:
-
您希望
c.get().print()输出“C\nB\n”还是希望您注释掉的行编译并提供“B\n”一半? -
后者。编辑了我的问题,希望更清楚。
-
你应该说明你想要这个。因为它是唯一可能的答案是:不,你不能。
-
我对命名参数习语的理解不同,你的所有这些方法都会返回
AlertBox&,如果他们碰巧在后台使用了其他对象/ctors,那就没关系了 -
Nope.. 如果我想创建一个 Window,或者从 Window 派生的另一个类怎么办。除非我想剪切和粘贴大量代码,否则相应的构建器将从他们的基础构建器继承,例如
AlertBoxBuilder : public WindowBuilder。如果我在创建一个Window,WindowBuilder的命名参数方法需要返回WindowBuilder&。如果我正在创建一个 AlertBox,他们需要返回 AlertBox&。这是 CRTP 可以解决的问题。