一、基于原则设计
在前面分析了应用于元编程中的策略(Policy),为了更好的把策略和策略设计分析清楚,本文重点分析一下基于原则设计或者说基于策略的设计(policy-based)。在前面的分析说已经说明,策略更倾向于行为。所以, 基于原则设计是以Policy为基础,结合模板元编程的一种设计模式。
二、分析和template template parameters
在前面的策略分析中,看到了一种代码,即在模板内的模板参数又是模板的这种情况。类似于下面的代码:
template <class T>
struct OpNewCreator
{
static T* Create()
{
return new T;
}
};
//非模板做为模板参数
template <class CreationPolicy>
class WidgetManager:public CreationPolicy
{......};
//模板做为模板参数
template <template<class Created> calss CreationPolicy>
class WidgetManager:public CreationPolicy<Widget>
{......};
//used
typedef WidgetManager<OpNewCreator> MyWidgetMgr;
//而不是原来直接使用模板
typedef WidgetManager<OpNewCreator<Widget>> MyWidgetMgr;
在策略设计中,为了防止类型的清晰表达和安全控制,就可以使用template template parameters,如果单纯的使用模板,则容易让使用者产生错误的使用,也不容易理解(如上面的代码)。如果使用模板做为模板的参数,就会很清晰的让使用者明白策略的意义和目的。模板的模板参数的使用主要有两种方式,一种是模板类做为模板参数;另外一种是模板函数做为模板参数。上面举个模板类的例子,下面看一下模板函数的例子:
struct OpNewCreator
{
template<Class T>
static T * Create()
{
return new T;
}
};
这种应用的目的其实是对早期编译器的一种兼容,但一般不再推荐。
使用策略的优势在于,利用policy classes可以更加灵活的形成接口,它是语法导向(Syntax Oriented)而非标记导向(Signature Oriented)。这就引出了它和继承的不同,继承是强规范型的,反复的继承会还会导致代码的膨胀甚至有可能造成代码的污染(不小心调用了没用实现的虚函数,产生了意想不到的后果)。而policy classes不然,它更松散,可以随机组成,它不会产生代码的膨胀(当然你要把模板的代码膨胀加进来也算),更安全。另外多重继承也缺乏具体的类型的信息,而在策略类的应用下,会明确的指定最终使用的策略的类的信息。
所以这就指出一个应用的方向,把二者结合起来,扬长避短。既可以动态的灵活的从外部更改policies又可以根据自己的的实际场景提供相关的policies。看上面的代码是不是有些熟悉,对,就是使用已经分析过的CRTP,不清楚的可以翻一翻前面的文章。
三、例程
经过上面的分析,可以看一个例程来加深一下印象:
#include <iostream>
#include <string>
template <typename OutputPolicy, typename LanguagePolicy>
class HelloWorld : private OutputPolicy, private LanguagePolicy {
public:
// Behavior method.
void Run() const {
// Two policy methods.
Print(Message());
}
private:
using LanguagePolicy::Message;
using OutputPolicy::Print;
};
class OutputPolicyWriteToCout {
protected:
template <typename MessageType>
void Print(MessageType&& message) const {
std::cout << message << std::endl;
}
};
class LanguagePolicyEnglish {
protected:
std::string Message() const { return "Hello, World!"; }
};
class LanguagePolicyGerman {
protected:
std::string Message() const { return "Hallo Welt!"; }
};
int main() {
// Example 1
using HelloWorldEnglish = HelloWorld<OutputPolicyWriteToCout, LanguagePolicyEnglish>;
HelloWorldEnglish hello_world;
hello_world.Run(); // Prints "Hello, World!".
// Example 2
// Does the same, but uses another language policy.
using HelloWorldGerman = HelloWorld<OutputPolicyWriteToCout, LanguagePolicyGerman>;
HelloWorldGerman hello_world2;
hello_world2.Run(); // Prints "Hallo Welt!".
}
注:代码引自:https://en.wikipedia.org/wiki/Modern_C%2B%2B_Design#Policy-based_design
四、总结
在上一篇中,重点讲的是policy和policy class,而本篇重点是分析一下policy-based的用法,其实后者前者的一种综合的动用。元编程中的Policy-Based Class Design 既可以规避多重继承的缺点又可以利用模板技术的优点。这其实是给了一个编程的思维方式,要善于整合利用不同的技术来实现一个更优的方法。
工程创新也是创新。