我在“好奇重复模板模式”上阅读的所有内容似乎都是一层继承,即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.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)可能会返回没有WindowParameter(int)函数的GuiElementOptions&。
以前是asked,解决方案似乎是Curiously Recurring Template Pattern的某种味道。我使用的特殊口味是here。
每次我创建一个新的Gui元素时都要编写很多代码。我一直在寻找简化方法。造成复杂性的主要原因是我正在使用CRTP解决命名参数成语问题,但我有三层而不是两层(GuiElement,Window和AlertBox),而current workaround的类数是我的四倍。 (!)例如,Window,WindowOptions,WindowBuilderT和WindowBuilder。
这使我想到了一个问题,在这个问题上,我实质上是在寻找一种更优雅的方式在长继承链(例如GuiElement,Window和Alertbox)上使用CRTP。
最佳答案
对于您希望达成的目标,我还不太清楚,但这与您的要求非常接近。
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
还不存在。