2011-10-22 12 views
11

Chcę używać idiomu pimpl, aby uniknąć sytuacji, w której użytkownicy mojej biblioteki potrzebują naszych zewnętrznych zależności (np. Doładowania itp.), Ale kiedy moja klasa jest szablonowana, co wydaje się niemożliwe, ponieważ metody muszą być w nagłówek. Czy jest coś, co mogę zrobić zamiast tego?pimpl dla szablonowej klasy

Odpowiedz

7

Jeśli klasa jest szablonem, użytkownicy zasadniczo muszą ją skompilować (i jest to dosłownie prawdziwe w najczęściej używanych implementacjach C++) i dlatego potrzebują twoich zewnętrznych zależności.

Najprostszym rozwiązaniem jest umieszczenie większości implementacji klasy w klasie podstawowej bez szablonu (lub zamkniętym elemencie członkowskim jakiejś klasy). Rozwiąż tam problem ukrywania się w module.

Następnie wpisz szablon klasy pochodnej (lub otaczającej), aby dodać do niej bezpieczeństwo typu.

Na przykład, załóżmy, że masz szablon, który zapewnia niesamowitą zdolność do przeznaczenia na pierwszym dostępu (pominięciem koniecznych konstruktor kopiujący, cesja, destructor):

template <class T> 
class MyContainer 
{ 
    T *instance_; 

public: 
    MyContainer() : instance_(0) {} 

    T &access() 
    { 
     if (instance_ == 0) 
      instance_ = new T(); 

     return *instance_; 
    } 
}; 

Jeśli chciał „logika” być podzielone na klasy bazowej bez szablonu, trzeba parametryzacji zachowania w sposób non-szablonu, który jest powiedzieć, jak używać funkcji wirtualna:

class MyBase 
{ 
    void *instance_; 

    virtual void *allocate() = 0; 

public: 
    MyBase() : instance_(0) {} 

    void *access() 
    { 
     if (instance_ == 0) 
      instance_ = allocate(); 

     return instance_; 
    } 
}; 

Następnie można dodać typu świadomości w warstwie zewnętrznej:

template <class T> 
class MyContainer : MyBase 
{ 
    virtual void *allocate() 
     { return new T(); } 

public: 
    T &access() 
     { return *(reinterpret_cast<T *>(MyBase::access())); } 
}; 

tj. Użytkownik korzysta z funkcji wirtualnych w celu umożliwienia szablonowi "wypełnienia" operacji zależnych od typu. Oczywiście ten wzorzec miałby naprawdę sens tylko wtedy, gdybyś miał jakąś logikę biznesową, wartą wysiłku ukrywania się.

+0

myślę, że to podejście może być również przydatna, jeśli nie chcesz, aby Twoje definicje preprocesora ('# define'), stałe itd., Aby były widoczne. Jako programista nie chcę widzieć szczegółów implementacji klasy/biblioteki, której używam, szczególnie na liście autouzupełniania. Możesz je ukryć, nawet jeśli twoja logika biznesowa jest niegodna ukrywania się. – mostruash

1

Można jawnie tworzyć instancje szablonów w pliku źródłowym, ale jest to możliwe tylko wtedy, gdy wiesz, jaki będzie typ szablonu. W przeciwnym razie nie używaj idiomu pimpl dla szablonów.

coś takiego:

header.hpp:

#ifndef HEADER_HPP 
#define HEADER_HPP 

template< typename T > 
class A 
{ 
    // constructor+methods + pimpl 
}; 

#endif 

source.cpp:

#include "header.hpp" 

// implementation 

// explicitly instantiate for types that will be used 
template class A<int>; 
template class A<float>; 
// etc... 
+0

-1 Nie używaj 'auto_ptr' dla PIMPL (jest to ** niezdefiniowane zachowanie ** do tworzenia' auto_ptr' z niekompletnym typem). Działa, jeśli zdefiniujesz zarówno konstruktor, jak i destruktor dla klasy zewnętrznej. Ale w takim przypadku nie potrzebujesz inteligentnego wskaźnika. –

+0

@ AlfP.Steinbach unique_ptr? Czy tylko surowy wskaźnik? –

+1

Tak, oba OK (chociaż nie jestem pewien co do szczegółów użycia 'unique_ptr' w tym przypadku, po prostu użyłbym' shared_ptr' i zaakceptowałbym obciążenie jako koszt niewymagający odczytywania drobnego druku w standard). Pozdrawiam, –

1

Istnieją dwa główne rozwiązania:

  • natomiast interfejs zależy od tego ja typ T, odradza się do słabiej wpisanej implementacji (np. jeden używający wskaźników bezpośrednio lub wykreślenie typu koryta) lub

  • obsługuje tylko określoną i dość ograniczoną liczbę typów.

Drugie rozwiązanie dotyczy np. char/wchar_t - niezależne elementy.

Pierwsze rozwiązanie było dość powszechne we wczesnych dniach szablonów C++, ponieważ w tamtym czasie kompilatory nie były dobre w rozpoznawaniu podobieństw w generowanym kodzie maszynowym, i wprowadzałyby kod “, który rozkwitłby pod numerem ”. Dzisiaj, ku zaskoczeniu każdego nowicjusza, który próbuje go rozwiązać, rozwiązanie szablonowe może często mieć mniejszy ślad kodu maszynowego niż rozwiązanie bazujące na polimorfizmie środowiska wykonawczego. Oczywiście, YMMV.

Cheers & HTH.,

+0

Ktoś próbował edytować, sugerując usunięcie "bezpośrednio lub poprzez usunięcie typu" i "konkretne i ..." kwalifikacja. Opracowanie jest niezbędne dla znaczenia, a kwalifikacja jest konieczna dla poprawności. Obecna odpowiedź "zaakceptuj jako odpowiedź" jest przykładem "bezpośrednio". Nie ma jeszcze przykładu "wymazywania typu", a jedyną wzmianką jest w tej odpowiedzi; byłoby wstydem, gdyby ktoś usunął to. –

Powiązane problemy