将 pimpl 与 Templated Class 和显式实例化的模板一起使用

Posted

技术标签:

【中文标题】将 pimpl 与 Templated Class 和显式实例化的模板一起使用【英文标题】:Using pimpl with Templated Class and explicitly instantiated templates 【发布时间】:2013-08-13 08:01:03 【问题描述】:

当我显式实例化模板时,如何将 pimpl 用于模板化类?

我只需要一个示例代码。

我试过的是:

// MyTemplatedClass.h

template< class T >
class MyTemplatedClass

private:
    class Impl;
    Impl* _pimpl;

public:
    void PublicMethod();

我的实现如下:

// MyTemplatedClass.cpp

template< class T >
class MyTemplatedClass<T>::Impl

    public:
        void PublicMethod();


template <class T>
void MyTemplatedClass<T>::Impl::PublicMethod()

    ...

将方法调用转发到实现类:

template< class T >
void MyTemplatedClass<T>::PublicMethod()

    _pimpl->PublicMethod();

显式实例化: 带 int 和 double 的示例:

template class MyTemplatedClass< int >;
template class MyTemplatedClass< double >;

但它似乎不起作用。

【问题讨论】:

当某件事“[似乎]不起作用”时,总是准确地告诉我们如何它“[似乎]不起作用” 。”换句话说,您遇到了什么错误? 当我尝试转发 PublicMethod 时,编译器没有检测到我的 PublicMethod。 _pimpl-> 应该给我一个可用方法的列表,但没有一个存在。 你说的是你的IDE,对吧? IDE 和编译器中的代码分析通常是两件不同的事情;如果您的 IDE 只是没有向您显示某个方法可用,那并不一定意味着如果您在那里使用它,编译器就不会找到它。 它给了我错误:C2227:'->PublicMethod' 的左边必须指向类/结构/联合/通用类型 1> MyTemplatedClass.cpp(65):编译类模板成员函数时void MyTemplatedClass::PublicMethod()' @user1507569 似乎编译器找不到_pimpl 的正确定义。尝试在其前面加上 this-&gt; 【参考方案1】:

这将回答您的问题,但我怀疑它是否达到了您希望达到的效果。我怀疑您希望在 MyTemplatedClass 范围之外声明模板实现。从模板实现继承而不是将其作为成员变量可能是更好的设计。

如果你的编译器不支持外部模板声明,我看不出有一个指向实现的模板指针会增加任何价值。毕竟,无论如何,您都必须在头文件中隐藏您想要隐藏的实现细节。

#include <iostream>

template < class T > class MyTemplatedClass 
private:
  template < class U> class Impl 
  public:
     void ImplPublicMethod() 
           std::cout << "Standard implementation" << std::endl;
           
  ;

  Impl<T> * _pimpl;
public:
  MyTemplatedClass() : _pimpl(new Impl<T>)  
  ~MyTemplatedClass()  delete _pimpl; 
  void publicMethod() 
     _pimpl->ImplPublicMethod();
  
;

template<> class MyTemplatedClass<int> 
private:
  class Impl 
  public:
     void ImplPublicMethod() 
          std::cout << "Integer specialisation" << std::endl;
     ;
 ;

 Impl * _pimpl;
public:
  MyTemplatedClass() : _pimpl(new Impl)  
  ~MyTemplatedClass()  delete _pimpl; 
  void publicMethod() 
     _pimpl->ImplPublicMethod();
  
;

int main(int argc, char ** argv) 

   MyTemplatedClass<char> charVersion;
   charVersion.publicMethod();

   MyTemplatedClass<int> intVersion;
   intVersion.publicMethod();

   return 0;

【讨论】:

我一定是脑出血了。一个小小的错字让编译器很生气。我在问题中编写的代码运行良好。【参考方案2】:

模板类的方法总是必须在头文件中定义。您不能将 MyTemplatedClass.cpp 单独作为编译单元。您可以做的是#include 包含MyTemplatedClass.h 末尾的方法定义的文件,以便声明和定义至少在文件级别分开。所以你的问题可以通过添加来解决

#include "MyTemplatedClass.cpp"

MyTemplatedClass.h的末尾。

我在自己的代码中使用带有模板类的 pimpls,它对我有用。您的代码看起来很正确 - 我会为 pimpl 使用 std::unique_ptr,但我认为您的操作方式没有任何问题。

【讨论】:

-1:模板类的成员函数总是必须在头文件中定义除非它们在程序中的某个地方显式实例化。 OP 正在执行显式实例化,所以这不适用于此处。

以上是关于将 pimpl 与 Templated Class 和显式实例化的模板一起使用的主要内容,如果未能解决你的问题,请参考以下文章

pImpl设计如何将文件编译关系降低

pImpl设计如何将文件编译关系降低

如何将 pimpl 成语与 Qt 和 QObject 的子类一起使用

将 const std::unique_ptr 用于 pimpl 习惯用法

pimpl idiom

将使用 PIMPL 习语的类存储在 std::vector 中