【问题标题】:Is there any advantage to the pimpl idiom with a templated class?带有模板类的 pimpl 习语有什么优势吗?
【发布时间】:2011-09-26 23:09:54
【问题描述】:

据我了解,pimpl 习惯用法的主要好处是将数据成员隐藏在实现文件中而不是标题中。但是,模板需要在头文件中完全定义,以便编译器按需实例化它们。在这种情况下,对模板类使用 pimpl 习语有什么好处吗?

【问题讨论】:

    标签: c++ templates pimpl-idiom


    【解决方案1】:

    虽然 pimpl 习惯用法在模板类中使用时并没有真正隐藏任何内容,但它确实允许您轻松编写非抛出交换(尽管使用 C++11 移动语义,这不是一个问题)。

    【讨论】:

    • +1 完全忘记了这一点。除了不抛出的清洁度之外,它也是交换的效率优势。这也适用于移动语义,因为不透明类不像 pimpled 类那样容易移动。
    • 接受,因为这似乎是唯一真正的好处。
    【解决方案2】:

    在大型项目中,单独解耦翻译单元就是 pimpl 的充分理由。这甚至适用于模板:

    // main interface
    
    template <typename> struct MyImpl;
    
    class TheInterface
    {
      MyImpl<int> * pimpl;
    };
    

    // implementation
    
    #include "MyImpl.hpp" // heavy-weight template library
    
    // TheInterface implementation
    

    【讨论】:

    • 不太好,尽管您可以将主模板的实现与类定义分开,并在该 TU 中使用显式实例化。不过,这只有在您有少量、固定数量的模板参数时才有意义。但否则整个 pimpl 的想法可能并不那么合适。
    • 在大型项目中,pimpl 成语是使项目更大的好方法。结果是一种用 C++ 编码的 C# 程序,在这种情况下,最好直接移植到 C# 而不是 pimling。请注意,pimpl 习惯用法的编译速度改进在预编译头文件已经处理的情况下将毫无用处。 pimpl 是反 C++
    • 很遗憾,使用 std::unique_ptr>* pimpl 不起作用,必须使用原始指针
    【解决方案3】:

    有一种情况可能不是严格意义上的 pimpl 成语,但足够相似,值得了解。那就是在非类型安全版本上使用类型安全的模板包装器。

    class MapBase
    {
       public:
         void* getForKey(const std::string & k);
         void setForKey(const std::string & k, void * v);
         ...
    };
    
    template<typename T>
    class MyMap
    {
      public:
        T* getForKey(const std::string &k) { return (T*)base_.getForKey(k); }
        void setForKey( const std::string &k, const T* v) { base_.setForKey(k, T*v); }
      private:
       MapBase base_;
    };
    

    现在,MyMap&lt;T&gt; 的任何使用都不需要暴露给 MapBase 的内部,您只需获得这些功能的一个实现。我还考虑让 MapBase 成为一个抽象基类,以使解耦更加强大。

    正如我所说,它并不完全是一个 pimpl,但它以类似的方式解决了可能的相同问题。

    【讨论】:

      【解决方案4】:

      我认为,如果你稍微扩展一下这个习语,在某些情况下你至少可以从中得到一点启发。在模板中,并非每个操作都必须依赖于模板参数。因此,您可以从 Impl 类继承,该类本身使用 pimpl 习惯用法,如下所示:

      struct FooPimpl;
      
      class FooImpl {
        protected:
          FooPimpl* pimpl;
        public:
          void myCommonInterfaceMethod();
      };
      
      template <typename T> class Foo : public FooImpl {
        // stuff that depends on T
      };
      

      当然,这在很大程度上取决于具体情况。但我看到 pimpl 习惯用法在模板类上下文中工作。

      【讨论】:

        【解决方案5】:

        它仍然非常有用 - 考虑一个自动或共享指针,它是一个模板,非常适用于解决和实现 PIMPL。是否是最好的解决方案取决于问题。

        模板需要在头文件中完全定义,以便编译器按需实例化它们。

        您可以声明模板的成员和接口,然后在您的类型的 cpp 文件中,您可以#include 所需的特化定义并在那里使用它们。

        【讨论】:

          猜你喜欢
          • 2020-12-19
          • 1970-01-01
          • 2017-09-09
          • 2019-11-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-02
          • 2016-01-09
          • 2014-11-13
          相关资源
          最近更新 更多