我想使用pimpl习惯用法来避免让我的库的用户需要我们的外部依赖(比如boost等),但是当我的类被模板化时似乎是不可能的,因为这些方法必须在标题中。我可以做些什么吗?
答案 0 :(得分:9)
如果该类是模板化的,那么您的用户基本上需要编译它(这在最广泛使用的C ++实现中确实如此),因此它们需要您的外部依赖项。
最简单的解决方案是将类的大部分实现放在非模板基类(或某些类的封装成员对象)中。解决那里的模块隐藏问题。
然后编写模板派生(或封闭)类以向其添加类型安全性。
例如,假设您有一个模板,它提供了在第一次访问时分配的惊人功能(省略必要的复制构造函数,赋值,析构函数):
template <class T>
class MyContainer
{
T *instance_;
public:
MyContainer() : instance_(0) {}
T &access()
{
if (instance_ == 0)
instance_ = new T();
return *instance_;
}
};
如果您希望将“逻辑”分成非模板基类,则必须以非模板方式对行为进行参数化,也就是说,使用虚函数:
class MyBase
{
void *instance_;
virtual void *allocate() = 0;
public:
MyBase() : instance_(0) {}
void *access()
{
if (instance_ == 0)
instance_ = allocate();
return instance_;
}
};
然后,您可以在外层添加类型感知:
template <class T>
class MyContainer : MyBase
{
virtual void *allocate()
{ return new T(); }
public:
T &access()
{ return *(reinterpret_cast<T *>(MyBase::access())); }
};
即。您可以使用虚函数来允许模板“填写”与类型相关的操作。显然,如果你有一些值得隐藏的商业逻辑,这种模式才会真正有意义。
答案 1 :(得分:2)
您可以在源文件中显式实例化模板,但只有在您知道模板类型的情况下才可以实现。否则,不要将pimpl习语用于模板。
这样的事情:
header.hpp:
#ifndef HEADER_HPP
#define HEADER_HPP
template< typename T >
class A
{
// constructor+methods + pimpl
};
#endif
source.cpp:
#include "header.hpp"
// implementation
// explicitly instantiate for types that will be used
template class A< int >;
template class A< float >;
// etc...
答案 2 :(得分:1)
有两种通用解决方案:
虽然接口依赖于某种类型T
,但它依赖于更弱类型的实现(例如直接使用void*
指针或通过类型擦除)或
您只支持特定且数量非常有限的类型。
第二种解决方案与例如char
/ wchar_t
- 依赖的东西。
第一个解决方案在C ++模板的早期很常见,因为当时编译器不擅长识别生成的机器代码中的共性,并会引入所谓的“代码膨胀”。今天,对于任何试图尝试它的新手而言,模板化解决方案通常比依赖运行时多态性的解决方案具有更小的机器代码占用空间。当然,YMMV。
干杯&amp;第h。,