2013-03-20 8 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' –

+0

@KirilKirov: переместив все определения в файл заголовка, я бы отказался от любых улучшений скорости во время компиляции, и все вызывающие элементы заголовка должны были быть перекомпилированы при изменении Impl просто потому что это шаблонный класс (по сравнению с описанием из библиотеки MSDN). Должен ли я использовать идиому pimpl для развязки частей? –

+0

взгляните на это: http://herbsutter.com/gotw/_101/ –

ответ

1

У меня была аналогичная проблема - у нас есть базовый класс в нашей системе, называемый NamedComponent, и я хотел создать шаблон, который берет существующий именованный компонент и преобразует его в фасад подкладки.

Что я сделал, было разделение шаблона на заголовок и встроенный файл и создание функции для создания экземпляра шаблона. Это позволяет реализовать реализацию в библиотеке с использованием шаблонных экземпляров фасада с этой реализацией и для того, чтобы клиент мог использовать фасад на основе шаблона и форвардное объявление реализации.

заголовок 'foo.h': Foo

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 - включают заголовок шаблона, вперед провозглашают осуществления и использовать Pimpl фасад:

#include "Foo.h" 
class ParticularImpl; 

int main() { 
    Foo<ParticularImpl> bar; 

    bar.DoIt(); 
} 

Вы, возможно, придется возиться с содержимым функции его InstantiateFoo, чтобы заставить компилятор инстанцирует все FUNCT ионы - в моем случае база называла все функции pimpl в методах шаблонов, поэтому, как только на них ссылались, все они были. Вам не нужно вызывать функции Instantiate, просто ссылайтесь на них.

+0

Так что спасибо за ваш вклад. У меня были проблемы с вашими экземплярами, поэтому я решил придерживаться предложения Майкла Уайлда, чтобы явно создать экземпляр для каждого из моих типов, таких как 'template class Foo ; шаблонный класс Foo ; 'в встроенном файле, и он работает без ваших вспомогательных методов. –

1

IMHO PIMPL не имеет большого смысла с шаблонами, если вы не знаете все возможные параметры шаблона, и этот набор довольно мал. Проблема заключается в том, что в противном случае вы либо будете иметь реализацию Impl в файле заголовка, как было отмечено в комментариях. Если количество возможных параметров T невелико, вы все равно можете пойти с разделением, но вам нужно объявить специализации в заголовке, а затем явно создать экземпляр в исходном файле.

Теперь, чтобы ошибка компилятора: unique_ptr<Impl> требует определения Impl. Вам нужно будет напрямую использовать new и delete в ctor Foo::Foo и dtor Foo::~Foo соответственно и отказаться от удобства/безопасности интеллектуальных указателей.

+0

К счастью, у меня было бы только полдюжины поддерживаемых специализаций, где я хотел бы придерживаться PIMPL, который сделает разделение возможным. Мне, вероятно, придется попробовать еще несколько подходов, пока я не получу хороший результат. Спасибо, что напомнили мне о явных специализациях. –