1

正如 MSDN library here中所述,我想尝试一下 pimpl idiom。现在我有Foo.hpp一个

template<typename T>
class Foo {
public:
    typedef std::shared_ptr<Foo<T>> Ptr;

    Foo();
private:
    class Impl;
    std::unique_ptr<Impl> pImpl;
};

尚未T使用参数的地方。实现存储在Foo.cpp

template<typename T>
class Foo<T>::Impl {
public:
    int m_TestVar;
};

template<typename T>
Foo<T>::Foo() : pImpl(new Impl) {
    this->pImpl->m_TestVar = 0x3713;
}

目前编译器有两个错误和一个警告:

  • use of undefined type 'Foo<T>::Impl'; ... vc\include\memory in line 1150
  • can't delete an incomplete type; ... vc\include\memory in line 1151
  • deletion of pointer to incomplete type 'Foo<T>::Impl'; no destructor called; ... vc\include\memory in line 1152

这里有什么冲突,我该如何解决?

编辑。删除了对std::make_shared基于一个旧版本的复制和粘贴失败的调用。

4

2 回答 2

2

我有一个类似的问题——我们的系统中有一个基类,NamedComponent我想创建一个模板,它采用现有的命名组件并将其转换为 pimpl 外观。

我所做的是将模板分成一个头文件和一个内联文件,并创建一个函数来使模板被实例化。这允许实现位于库中,具有该实现的外观的模板实例化,并且客户端能够基于模板和实现的前向声明使用外观。

标题“Foo.h”:

template<class T> class Foo
{
public:
    Foo ();
    virtual ~Foo();

private:
    T *impl_;

public:
    // forwarding functions
    void DoIt();
};

内联函数“Foo.inl”:

#include "Foo.h"

template<class T> Foo<T>::Foo() :
    impl_ ( new T )
{
}

template<class T> Foo<T>::~Foo()
{
    delete impl_;
}

// forwarding functions
template<class T> void Foo<T>::DoIt()
{
    impl_ -> DoIt();
}    

// force instantiation
template<typename T>
void InstantiateFoo()
{
    Foo<T> foo;
    foo.DoIt();
}

实现 cpp 文件 - 包括模板内联函数,定义实现,引用实例化函数:

#include "Foo.inl"

class ParticularImpl {
public:
    void DoIt() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

void InstantiateParticularFoo() {
    InstantiateFoo<ParticularImpl>();
}

客户端 cpp 文件 - 包含模板头,转发声明实现并使用 pimpl 外观:

#include "Foo.h"
class ParticularImpl;

int main () {
    Foo<ParticularImpl> bar;

    bar.DoIt();
}

您可能不得不摆弄 InstantiateFoo 函数的内容以强制编译器实例化所有函数 - 在我的例子中,基在模板方法中调用了所有 pimpl 函数,因此一旦引用了一个,它们都是。您不需要调用 Instantiate 函数,只需链接到它们。

于 2013-03-20T14:06:30.073 回答
1

恕我直言 PIMPL 对模板没有多大意义,除非您知道所有可能的模板参数并且该集相当小。问题是,您要么Impl在头文件中拥有实现,否则正如评论中所指出的那样。如果可能的T参数数量很少,您仍然可以进行分离,但您需要在标头中声明特化,然后在源文件中显式实例化它们。

现在到编译器错误:unique_ptr<Impl>需要定义Impl可用。您需要分别在 ctor和 dtor中直接使用newand并放弃智能指针的便利性/安全性。deleteFoo::FooFoo::~Foo

于 2013-03-20T12:58:15.937 回答