最近我一直在思考如何设计一个特定的软件,我曾经在下面制作了以下部分:
template <typename ... Mixins>
class Foo : public virtual Mixins... {
/* ... */
};
我的想法是能够根据用户的需要使用其他属性或行为来扩充基类。假设一个应用程序需要使用带有标识号的Foo
s。也许其他一些应用程序需要能够用颜色来讨论Foo
s。可以通过添加以下类来满足这些需求:
class HasID {
int m_id = -1;
public:
int getID() { return m_id; }
void assignID(int id) { m_id = id; }
};
class HasColor {
public:
int color = 0;
};
关于此代码的两个问题如下:
这是一个额外的例子,展示了它可以打印增强类对象的细节。
打印功能:
// Default printBase
template <typename Base>
void printBase(std::ostream& out, Base& x) {}
// printBase for HasID
template <>
void printBase(std::ostream& out, HasID& x) {
out << ", ID=" << x.getID();
}
// printBase for HasColor
template <>
void printBase(std::ostream& out, HasColor& x) {
out << ", color=" << x.color;
}
// Recursive step of printBases
template <typename Derived, typename Base, typename ... Bases>
void printBases(std::ostream& out, Derived& x, Base& y) {
printBase(out, y);
printBases<Derived, Bases...>(out, x, x);
}
// Base case of printBases
template <typename Derived>
void printBases(std::ostream& out, Derived& x, Derived& y) {}
// ostream operator
template <typename ... Mixins>
std::ostream& operator<<(std::ostream& out, Foo<Mixins...>& x) {
out << "<Foo";
printBases<Foo<Mixins...>, Mixins...>(out, x, x);
return out << '>';
}
主:
int main()
{
Foo<> plainFoo;
Foo<HasID> fooWithID;
fooWithID.assignID(42);
Foo<HasID, HasColor> multiTalentedFoo;
multiTalentedFoo.assignID(1234);
multiTalentedFoo.color = 0xff0000;
std::cout
<< plainFoo << '\n'
<< fooWithID << '\n'
<< multiTalentedFoo << '\n';
}
输出:
<Foo>
<Foo, ID=42>
<Foo, ID=1234, color=16711680>
答案 0 :(得分:2)
关于此代码的两个问题如下:
此特定模式是否有名称?
使用这样的模板有什么用途和缺点?
CRTP“奇怪地反复出现模板模式”或有时也称为“mixin”。
https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
我们真的想再次讨论这里的缺点,而网络知道这种模式/成语吗?讨论得足够多了: - )
https://softwareengineering.stackexchange.com/questions/123886/is-crtp-used-much-and-why-it-is-isnt
这篇文章http://www.drdobbs.com/building-more-flexible-types-with-mixins/184402056注意到:
CRTP技术的最大缺点是构造函数不是继承的。这意味着如果在实现类中使用初始化构造函数,则每个扩展都必须具有适当的初始化构造函数。这导致扩展受到更多限制,因此不太有用。
对于c ++ 14来说,这不再是真的,你很有可能用可变参数模板和基类的构造函数调用链来杀死那个参数。继承&amp;委托构造函数也将有助于实现这一目的。
与所有模板一样,您必须记住每个实例化都是一种新类型,可能会导致可执行文件中出现大量代码重复。我经常使用这种模式并接受成本。另一种方法是手工编写可执行大小不小的代码。你必须为你需要的东西买单。
答案 1 :(得分:-1)
让我回答第二部分:是的,这种模式有一个名称:Static Polymorphism。