2011-10-22 78 views
11

我想使用pimpl成语,以避免我的图书馆的用户需要我们的外部依赖(如提升等),但是当我的类模板,似乎是不可能的,因为方法必须在头。有什么我可以做的呢?pimpl为模板类

回答

7

如果这个类是模板化的,那么你的用户基本上需要编译它(这在最广泛使用的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())); } 
}; 

即使用虚拟函数来允许模板“填写”依赖于类型的操作。显然,如果你有一些值得隐藏的业务逻辑,这种模式才有意义。

+0

我觉得这个方法可能是也非常有用,如果你不想让你的预处理器定义('#define'),常数等。可见。作为一名开发人员,我不想看到我正在使用的类/库的实现细节,特别是在自动完成列表中。即使您的业务逻辑不值得隐藏,您也可能希望隐藏这些内容。 – mostruash

1

您可以显式实例化源文件中的模板,但只有在知道模板类型将会是什么时才有可能。否则,不要使用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... 
+0

-1不要将'auto_ptr'用于PIMPL(它的**未定义行为**用不完整类型实例化'auto_ptr')。如果你为外部类定义了构造函数和析构函数,它确实会起作用。但在这种情况下,你不需要智能指针。 –

+0

@ AlfP.Steinbach unique_ptr?或者只是一个原始指针? –

+1

是的,都OK(虽然我不确定在这种情况下如何使用'unique_ptr'的细节;我只会使用'shared_ptr'并接受开销,因为不需要人们在标准)。干杯, –

1

一般有两种解决方案:

  • 而接口取决于如此我输入了T,它按照更弱的类型实现(例如,一个使用void*指针直接或槽式擦除),或者

  • 您只支持特定且数量有限的类型。

第二种解决方案与例如, char/wchar_t-依赖的东西。

第一种解决方案在C++模板的早期非常普遍,因为那时编译器不擅长识别生成的机器代码中的共同点,并且会引入所谓“代码膨胀”。今天,任何尝试新手的人都会惊讶,模板化解决方案的机器代码足迹通常比依赖运行时多态性的解决方案要小。当然,YMMV。

干杯&心连心,

+0

有人试图编辑建议删除“直接或通过类型擦除”阐述和“特定和...”资格。阐述对于意义是必要的,并且对于正确性来说资格是必需的。目前被接受为解决方案的答案是“直接”的一个例子。目前还没有“类型删除”的例子,唯一值得一提的是这个答案;如果某人成功删除了该文件,那将是一件耻辱。 –