给定两个非常相似但行为不同的模板化类:
template<class T>
firstBase {};
template<class T>
secondBase {};
现在我有了这个其他类,基于它的模板参数,它将来自firstBase
或来自secondBase
:
template<class B, class T>
myClass : public B<T> { /* T is used in here */ };
嗯,这不起作用。编译器告诉我B
是一个未知的模板名称。 (error: unknown template name 'B'
)
我目前的解决方法是将myClass
定义为
template<class B, class T>
myClass : public B { /* T is used in here */ };
并且myClass
的来电者需要通过myClass<b<t>, t>
而不是myClass<b, t>
来取消它。
后者非常好,可以减少一些复制和粘贴代码。有没有其他方法可以实现这个目标?
在我的用例中,我正在尝试为pimpl习语实现deep_const_ptr
,以启用'true constness'。根据{{1}}是否需要复制 - 可分配,它可以使用myClass
或deep_const_ptr<std::shared_ptr>
作为其私有指针。
deep_const_ptr<std::unique_ptr>
修改
因此,我按照Luc Danton in his answer的建议,将#include <memory>
#include <iostream>
template<class pointerT, class typeT>
class deep_const_ptr : public pointerT
{
public:
explicit deep_const_ptr(typeT* ptr) : pointerT(ptr) { }
// overloading pointerT::operator->() for non-constant access
typeT* operator->() {
std::cout << "deep_const_ptr::operator->()" << std::endl;
return pointerT::operator->();
}
// overloading pointerT::operator->() for constant access
const typeT* operator->() const {
std::cout << "deep_const_ptr::operator->() const" << std::endl;
return pointerT::operator->();
}
};
或std::unique_ptr<myClass::Private>
传递给我的自定义std::shared_ptr<myClass::Private>
:
deep_const_ptr
答案 0 :(得分:2)
你想要的是template template parameter:
// vvvvvvvvvvvvvvvvvv
template<template<typename> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
然后,使用它:
myClass<firstBase,int> myIntClassObject;
myClass<secondBase,bool> myBoolClassObject;
对于std::unique_ptr
,您可以创建一个包装器:
template<typename T>
class uniquePtr : public std::unique_ptr<T>
{
};
或
template<typename T>
using uniquePtr = std::unique_ptr<T>;
答案 1 :(得分:1)
B
应该是模板模板参数:
template<template <class> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
现在您可以将firstBase
或secondBase
作为第一个模板参数,因为它们是模板。
答案 2 :(得分:1)
您只需使用template-template parameter即可使其发挥作用:
template <
template <class> class B, class T>
// ^^^^^^^^^^^^^^^^
class myClass : public B<T> { / ... / };
答案 3 :(得分:1)
您需要的是模板模板参数:
template<template<class> class B, class T>
// ^^^^^^^^^^^^^^^
class myClass : public B<T> { /* T is used in here */ };
现在,myClass
类模板的第一个模板参数必须是一个接受一个模板(类型)参数的类模板。所以,把所有东西放在一起:
template<class T>
class firstBase {};
template<class T>
class secondBase {};
template<template<class> class B, class T>
class myClass : public B<T> { /* T is used in here */ };
以下是如何实例化myclass
模板以创建派生自firstBase<int>
的类:
myClass<firstBase, int> obj;
最后,这是一个live example。
答案 4 :(得分:1)
我建议一般不要使用模板模板参数。例如,std::unique_ptr
和std::shared_ptr
的不同之处在于前者接受两个类型参数而后者只接受一个。所以如果你声明例如template<template<typename> class B, typename T> class foo;
然后foo<std::shared_ptr, int>
有效,但foo<std::unique_ptr, int>
不是。
在您的特定情况下,您可以使用template<typename...> class B
作为参数,因为这种模板模板参数是特殊的。但是,这只接受仅采用类型参数的模板(甚至不是模板模板参数)。有时这可以使用别名模板解决,有时它不能。
根据我的经验,有更好的选择 - 例如你可以有条件地继承:
template<typename T>
struct pointer: std::conditional</* Make a choice here*/, std::unique_ptr<T>, std::shared_ptr<T>>::type {};
或者为什么不接受智能指针作为参数本身:
template<typename Pointer>
struct foo {
Pointer pointer;
/* typename Pointer::element_type plays the role that T used to have */
};
标准库本身采取了一些措施来避免模板模板参数:您是否注意到std::vector<T>
使用std::allocator<T>
作为参数,而不是std::allocator
?作为权衡,这意味着分配器必须提供rebind
成员别名模板。