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

How to use a policy to add an additional layer of functionality in the class?

本文关键字:功能 添加 何使用 策略      更新时间:2023-10-16

最近,我不得不设计一个建立在另一个模板类之上的类。 种类:

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 & key, ValueType & value)似乎不正确。是否应该存在键控查找应该取决于用户将决定的某些策略 - 因此查找方法本身将根据策略存在。

如何最好地做到这一点?

有多种方法可以做到这一点,但我认为这可能对你有用。这个想法是你定义一个基类,你的类(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;
};

换句话说,上面指出,在A == std::mapA == std::unordered_map时应该定义一个find成员。在所有其他情况下,非专用模板是固定的,为此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);    
}

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

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

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