2013-03-20 64 views
1

正如MSDN庫here中所述,我想用pimpl習語進行實驗。現在我有一個Foo.hpp帶有pimpl習語的模板類不正確

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

什麼是這裏的concflict和我怎麼能解決它?

編輯。刪除了致電std::make_shared - 複製&根據一箇舊版本粘貼失敗。

+0

1.定義**必須**在頭文件中(由於模板)。 2.我沒有看到'Foo :: Foo'的定義 – 2013-03-20 12:38:07

+0

@KirilKirov:通過移動頭文件中的所有定義,我將放棄編譯過程中的任何速度改進,並且在更改Impl時必須重新編譯頭的所有調用者因爲它是用於模板類(與來自MSDN Library的描述相比較)。我仍然應該使用pimpl成語來分離部分? – 2013-03-20 12:46:06

+0

看看這個:http://herbsutter.com/gotw/_101/ – 2013-03-20 13:04:52

回答

1

我有類似的問題 - 我們在我們的系統中有一個名爲NamedComponent的基類,我想創建一個模板,它將現有命名組件轉換爲pimpl facade。

我所做的是將模板分隔成一個頭文件和一個內聯文件,並創建一個函數使模板實例化。這允許實現在一個庫中,使用該實現的外觀的模板實例化,並且客戶端能夠基於模板和實現的前向聲明來使用外觀。

頭 'foo.h中':

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

private: 
    T *impl_; 

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

內聯函數「富。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文件 - 包括模板頭部,向前聲明的實施和使用平普爾門面:

#include "Foo.h" 
class ParticularImpl; 

int main() { 
    Foo<ParticularImpl> bar; 

    bar.DoIt(); 
} 

您可能必須與InstantiateFoo功能的內容擺弄強制編譯器來實例化的所有本功能離子 - 在我的情況下,基地在模板方法中調用所有pimpl的函數,所以一旦被引用,它們都是。你不需要調用Instantiate函數,只需鏈接到它們。

+0

非常感謝您的意見。我遇到了你的實例化問題,所以我決定堅持Michael Wilds的建議,爲我的每個類型明確地實例化這個,比如'template class Foo ;模板類Foo ;'在內聯文件中,它的工作方式沒有你的幫助器方法。 – 2013-03-21 14:57:38

1

恕我直言,PIMPL對模板沒什麼意義,除非你知道所有可能的模板參數,而且這個設置相當小。問題是,否則您將在頭文件中實現Impl實現,如註釋中所述。如果可能的T參數的數量很少,您仍然可以使用分隔符,但是您需要在頭文件中聲明特化文件,然後在源文件中顯式實例化它們。

我們的編譯器錯誤:unique_ptr<Impl>需要Impl定義可用。您需要分別直接使用newdelete,並分別使用智能指針的便利性/安全性,然後分別使用Foo::Foo和dtor Foo::~Foo

+0

幸運的是,我最終只有半打支持專業化,我想堅持PIMPL這將使分離可行。我可能需要嘗試更多的方法,直到獲得好的結果。感謝您提醒我明確的專業化。 – 2013-03-20 13:03:45