最近我不得不设计一个基于另一个模板类构建的类。种类:
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)似乎不正确。键控查找是否应该在那里应该依赖于用户将决定的某些策略 - 因此查找方法本身将根据策略存在。
如何做到最好?
答案 0 :(得分:1)
有多种方法,但我认为这可能适合您。我们的想法是定义一个基类,您的类(B
)从该基类继承。基类是具有一个bool参数的类模板,并且根据此参数的值,它确定或不定义查找成员。作为模板参数传递的值取决于您为几种可能类型的A
定义的类型特征。
警告:下面的模板元编程。
让我们从类型特征开始。我假设可能的A
类型(模板模板参数)类似vector
,map
等等:
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;
};
在文字中,上述内容表明在find
或A == 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
{}
};
因为不同的容器类型(如vector
和map
)需要不同数量的模板参数,所以我通过将类模板作为可变参数模板来略微推广它。它现在可以存储任何类型的容器。它继承自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; };