2

给定两个非常相似但行为不同的模板类:

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>.

后者会非常好,并减少一些复制和粘贴代码。有没有其他方法可以实现这一目标?


在我的用例中,我正在尝试deep_const_ptr为 pimpl idiom 实现一个启用“true constness”的方法。根据是否myClass需要可复制分配,它要么使用deep_const_ptr<std::shared_ptr>要么deep_const_ptr<std::unique_ptr>用于其私有指针。

#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->();
    }
};

编辑因此,正如Luc Danton 在他的回答中
所建议的那样,我最终通过或传递给我的习惯:std::unique_ptr<myClass::Private>std::shared_ptr<myClass::Private>deep_const_ptr

template<typename pointerTypeT>
class deep_const_ptr : public pointerTypeT {
  explicit deep_const_ptr(typename pointerTypeT::element_type* ptr) : pointerTypeT(ptr);
  typename pointerTypeT::element_type* operator->();
  const typename pointerTypeT::element_type* operator->() const;
};

deep_const_ptr<std::unique_ptr<Test::Private>> d_unique;
deep_const_ptr<std::shared_ptr<Test::Private>> d_shared;
4

5 回答 5

2

你想要的是模板模板参数

//       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>;
于 2013-04-14T12:55:47.053 回答
1

我一般建议不要使用模板模板参数。例如std::unique_ptrstd::shared_ptr不同之处在于前者接受两个类型参数,而后者只接受一个。因此,如果您声明 eg template<template<typename> class B, typename T> class foo;thenfoo<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 */
};

标准库本身采取了一些措施来避免模板模板参数:你有没有注意到 anstd::vector<T>用作std::allocator<T>参数,而不是std::allocator?作为权衡,这意味着分配器必须提供rebind成员别名模板。

于 2013-04-14T13:12:56.047 回答
1

您只需使用模板模板参数即可使其工作:

template <
    template <class> class B, class T>
//  ^^^^^^^^^^^^^^^^
class myClass : public B<T> { / ... / };
于 2013-04-14T12:55:32.380 回答
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;

最后,这是一个活生生的例子

于 2013-04-14T12:55:57.567 回答
1

B应该是模板模板参数:

template<template <class> class B, class T>
class myClass : public B<T> { /* T is used in here */ };

现在您可以给出firstBasesecondBase作为第一个模板参数,因为它们是模板。

于 2013-04-14T12:55:21.113 回答