如何使用策略在类中添加额外的功能层?

时间:2015-01-10 19:23:51

标签: c++ templates design-patterns

最近我不得不设计一个基于另一个模板类构建的类。种类:

template<
  typename T1, 
  template <class> class A, 
  ..> 
class B { 
  A<T1> a1; 
  // other members; 
public:
  bool insert(T1 const & value);
  bool update(T1 const & value);
  bool delete(T1 const & value);
  bool contains(T1 const & value); 
};

我最初的计划是只提供价值语义,这个界面很好。最近的要求出现了,我的任务是提供除现有功能之外的键控查找。我正在寻找以干净的方式做到这一点的解决方案。

添加bool find(KeyType const&amp; key,ValueType&amp; value)似乎不正确。键控查找是否应该在那里应该依赖于用户将决定的某些策略 - 因此查找方法本身将根据策略存在。

如何做到最好?

1 个答案:

答案 0 :(得分:1)

有多种方法,但我认为这可能适合您。我们的想法是定义一个基类,您的类(B)从该基类继承。基类是具有一个bool参数的类模板,并且根据此参数的值,它确定或不定义查找成员。作为模板参数传递的值取决于您为几种可能类型的A定义的类型特征。

警告:下面的模板元编程。

让我们从类型特征开始。我假设可能的A类型(模板模板参数)类似vectormap等等:

template <template <typename ...> class Container>
struct EnableKeyFind
{
  static int const value = false; // fallback                                                                                                                                           
};

template <>
struct EnableKeyFind<std::map>
{
  static int const value = true;
};

template <>
struct EnableKeyFind<std::unordered_map>
{
  static int const value = true;
};

在文字中,上述内容表明在findA == std::map时应定义A == std::unordered_map成员。在所有其他情况下,非专用模板被实例化,value == false,即没有find - 成员。您可以添加其他专业来扩展此原则。

接下来,让我们定义基类:

template <typename Container, bool Enable>
class Base
{
  Container &d_cont; // Base-class should have access to the container                                                                                                                  
public:
  Base(Container &cont): d_cont(cont) {}

  template <typename KeyType, typename ValueType>
  bool find(KeyType const &key, ValueType const &val);

  /* An alternative implementation would be where you use 
     Container::key_type and Container::value_type and get rid of
     the function-template. This would require the Container-class 
     to have STL-like semantics. Pick whichever you prefer. */
};

// Specialization for 'false': no implementation                                                                                                                                        
template <typename Container>
class Base<Container, false>
{
public:
  Base(Container &cont) {}
};

基类专门用于Enable模板非类型参数为false的情况。在这种情况下,它的实现几乎是微不足道的(除了仍然需要相同签名的构造函数)。

现在让我们看看修改后的B类(我保留了非描述性名称以保持接近问题):

template <template <typename ...> class A, typename ... Types>
class B: public Base<A<Types ...>, EnableKeyFind<A>::value>
{
  typedef A<Types ...> AType;
  typedef Base<AType, EnableKeyFind<A>::value> BaseType;

  AType a1;
public:
  B():
    BaseType(a1) // provide the base with access to a1
  {}
};

因为不同的容器类型(如vectormap)需要不同数量的模板参数,所以我通过将类模板作为可变参数模板来略微推广它。它现在可以存储任何类型的容器。它继承自Base,并使用类型特征EnableKeyFind<A>来确定它是应该继承自Base<Container, true>还是(专业化)Base<Container, false>

一个典型的用例:

int main()
{
  B<vector, int> b1;
  b1.find(1, 1); // error: has no member find                                                                                                                                            

  B<map, string, int> b2;
  b2.find(1, 1);    
}

我希望这会有所帮助。无论如何,这是一个有趣的小练习。

此方法可能存在的缺点可能是,如果用户希望EnableKeyFind可用,则应为其特定容器专门设置find()类模板。我不知道这对您的用户来说是否太多了,但如果您想隐藏详细信息,您可以随时向他们提供宏:

define ENABLE_KEYFIND(Container) \
  template <>\
  struct EnableKeyFind<Container>\
  { static int const value = true; };