Jaka jest różnica między cechą a polisą?

Mam klasę, której zachowanie próbuję skonfigurować.

template<int ModeT, bool IsAsync, bool IsReentrant> ServerTraits;

Potem mam sam obiekt serwera:

template<typename TraitsT>
class Server {...};

Moje pytanie dotyczy mojego użycia powyżej Czy moje nazewnictwo jest błędnie nazwane? Czy mój parametr template rzeczywiście jest polityką, a nie cechą?

Kiedy argument szablonowy to cecha a polityka?

Author: TemplateRex, 2013-02-06

4 answers

Polityka

Zasady są klasami (lub szablonami klas), aby wprowadzić zachowanie do klasy nadrzędnej, zazwyczaj poprzez dziedziczenie. Poprzez rozkładanie interfejsu nadrzędnego na ortogonalne (niezależne) wymiary, klasy zasad tworzą elementy składowe bardziej złożonych interfejsów. Często spotykanym wzorcem jest dostarczanie zasad jako Definiowalnych przez użytkownika parametrów szablonu (lub szablonu-szablonu) z domyślną biblioteką. Przykładem z biblioteki Standardowej są Alokatory, które są parametrami szablonów zasad dla wszystkich kontenerów STL

template<class T, class Allocator = std::allocator<T>> class vector;

Tutaj, Allocator parametr szablonu (który sam w sobie jest również szablonem klasy!) wstrzykuje zasadę alokacji pamięci i dealokacji do klasy nadrzędnej std::vector. Jeśli użytkownik nie dostarczy alokatora, używana jest domyślna wartość std::allocator<T>.

Jak to jest typowe w polimorfizmie opartym na szablonach, wymagania interfejsu dla klas polityki są niejawne i semantyczne (oparte na poprawnych wyrażeniach), a nie jawne i składniowe (oparte na definicji wirtualnych funkcji członowych).

Zauważ, że nowsze, nieuporządkowane kontenery asocjacyjne mają więcej niż jedną politykę. Oprócz zwykłego parametru szablonu Allocator, przyjmują również zasadę Hash, która domyślnie jest obiektem funkcji std::hash<Key>. Pozwala to użytkownikom niestandardowych kontenerów skonfigurować je według wielu ortogonalnych wymiarów(alokacja pamięci i haszowanie).

Cechy

Cechy To szablony klas do wyodrębnij właściwości Z Typu generycznego. Istnieją dwa rodzaje cech: cechy jednowartościowe i cechy wielowartościowe. Przykładami cech o pojedynczej wartości są te z nagłówka <type_traits>

template< class T >
struct is_integral
{
    static const bool value /* = true if T is integral, false otherwise */;
    typedef std::integral_constant<bool, value> type;
};

Cechy jednowartościowe są często używane w template-metaprogramming i Sztuczkach SFINAE do przeciążania szablonu funkcji w oparciu o warunek typu.

Przykładami cech wielowartościowych są iterator_traits i allocator_traits z nagłówków <iterator> i <memory>, odpowiednio. Ponieważ cechy są szablonami klas, mogą być wyspecjalizowane. Poniżej przykład specjalizacji iterator_traits dla T*
template<T>
struct iterator_traits<T*>
{
    using difference_type   = std::ptrdiff_t;
    using value_type        = T;
    using pointer           = T*;
    using reference         = T&;
    using iterator_category = std::random_access_iterator_tag;
};

Wyrażenie std::iterator_traits<T>::value_type umożliwia użycie kodu ogólnego dla pełnowartościowych klas iteratora nawet dla surowych wskaźników (ponieważ surowe wskaźniki nie mają elementu value_type).

Interakcja między zasadami i cechami

Pisząc własne biblioteki generyczne, ważne jest, aby zastanowić się, w jaki sposób użytkownicy mogą specjalizować swoje własne szablony klas. Należy jednak uważać, aby nie pozwolić użytkownikom paść ofiarą jednej reguły definicji , używając specjalizacji cech do wstrzykiwania, a nie do wyodrębniania zachowań. Parafrazując ten stary post Andrei Alexandrescu

Podstawowym problemem jest to, że kod, który nie widzi wyspecjalizowanych wersja cechy nadal będzie się kompilować, prawdopodobnie będzie linkować, a czasami może nawet uciec. Dzieje się tak dlatego, że w przypadku braku explicit specjalizacja, niewykwalifikowany szablon, prawdopodobnie implementacja ogólnego zachowania, które działa w Twoim szczególnym przypadku jako cóż. W związku z tym, jeśli nie cały kod w aplikacji widzi ta sama definicja cechy, ODR jest naruszona.

C++11 std::allocator_traits unika tych pułapek, wymuszając, że wszystkie kontenery STL mogą wyodrębniać właściwości ze swoich polityk Allocator poprzez std::allocator_traits<Allocator>. Jeśli użytkownicy nie zdecydują się lub zapomną podać niektórych wymaganych członków polityki, Klasa traits może wejść i podać domyślne wartości dla brakujących członków. Ponieważ allocator_traits sama w sobie nie może być wyspecjalizowana, użytkownicy zawsze muszą przejść w pełni zdefiniowaną politykę alokacji w celu dostosowania alokacji pamięci kontenerów i nie mogą wystąpić żadne ciche naruszenia ODR.

Zauważ, że jako pisarz bibliotek nadal można specjalizować szablony klas cech (tak jak robi to STL w iterator_traits<T*>), ale dobrą praktyką jest przekazywanie wszystkich specjalizacji zdefiniowanych przez użytkownika poprzez klasy zasad do wielowartościowe cechy, które mogą wyodrębnić wyspecjalizowane zachowanie(jak robi to STL w allocator_traits<A>).

UPDATE : problemy z ODR w zdefiniowanych przez użytkownika specjalizacjach klas cech występują głównie wtedy, gdy cechy są używane jako globalne szablony klas i nie możesz zagwarantować, że wszyscy przyszli użytkownicy zobaczą wszystkie inne specjalizacje zdefiniowane przez użytkownika. Zasady są lokalnymi parametrami szablonu i zawierają wszystkie istotne definicje, dzięki czemu mogą być definiowane przez Użytkownika bez ingerencja w inny kod. Lokalne parametry szablonu zawierające tylko typ i stałe-ale bez funkcji behawioralnych-nadal mogą być nazywane "cechami", ale nie będą widoczne dla innych kodów, takich jak std::iterator_traits i std::allocator_traits.

 79
Author: TemplateRex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-08 15:27:03

Myślę, że znajdziesz najlepszą możliwą odpowiedź na swoje pytanie w książka Andrei Alexandrescu. Tutaj postaram się dać tylko krótki przegląd. Mam nadzieję, że to pomoże.


A Klasa cech jest klasą, która zazwyczaj ma być meta-funkcją powiązującą typy z innymi typami lub ze stałymi wartościami, aby zapewnić charakterystykę tych typów. Innymi słowy, jest to sposób na modelowanie właściwości typów . Mechanizm zwykle wykorzystuje szablony i specjalizację szablonów do zdefiniowania asocjacji:

template<typename T>
struct my_trait
{
    typedef T& reference_type;
    static const bool isReference = false;
    // ... (possibly more properties here)
};

template<>
struct my_trait<T&>
{
    typedef T& reference_type;
    static const bool isReference = true;
    // ... (possibly more properties here)
};

Metafunkcja cechy my_trait<> powyżej kojarzy Typ odniesienia T& i stałą wartość logiczną false do wszystkich typów T, które są , a nie same w sobie referencjami; z drugiej strony kojarzy Typ odniesienia T& i stałą wartość logiczną true do wszystkich typów T, które są referencjami {28]}.

Więc na przykład:

int  -> reference_type = int&
        isReference = false

int& -> reference_type = int&
        isReference = true

W kodzie możemy twierdzenie to jest następujące (wszystkie cztery linie poniżej skompilują się, co oznacza, że warunek wyrażony w pierwszym argumencie do static_assert() jest spełniony):

static_assert(!(my_trait<int>::isReference), "Error!");
static_assert(  my_trait<int&>::isReference, "Error!");
static_assert(
    std::is_same<typename my_trait<int>::reference_type, int&>::value, 
    "Error!"
     );
static_assert(
    std::is_same<typename my_trait<int&>::reference_type, int&>::value, 
    "Err!"
    );

Tutaj widać, że użyłem standardowego szablonu std::is_same<>, który sam w sobie jest meta-funkcją, która akceptuje dwa , a nie jeden argument typu. Sprawy mogą się tu dowolnie skomplikować.

Chociaż std::is_same<> jest częścią nagłówka type_traits, niektórzy uważają szablon klasy za klasę typu tylko wtedy, gdy działa on jako meta-predykat (w ten sposób akceptując jeden parametr szablonu). Z tego, co wiem, terminologia nie jest jednak jasno określona.

Przykład użycia klasy traits w bibliotece standardowej C++, spójrz na sposób, w jaki zostały zaprojektowane Biblioteki wejścia/wyjścia i biblioteki łańcuchów.


A Polityka jest czymś nieco innym (właściwie całkiem innym). Zwykle ma to być klasa, która określa, jakie zachowanie Inna, generyczna klasa powinna dotyczyć pewnych operacji, które mogą być potencjalnie realizowane na kilka różnych sposobów (a których implementacja jest zatem pozostawiona klasie policy).

Na przykład, OGÓLNA Klasa inteligentnego wskaźnika może być zaprojektowana jako klasa szablonu, która akceptuje politykę jako parametr szablonu do decydowania o tym, jak obsługiwać liczenie ref-jest to tylko hipotetyczny, zbyt uproszczony i ilustracyjny przykład, więc proszę spróbować wyciągnąć z tego konkretnego kod i skup się na mechanizmie .

, które pozwoliłyby projektantowi inteligentnego wskaźnika nie zobowiązywać się do kodowania na twardo, czy modyfikacje licznika odniesienia powinny być wykonywane w sposób bezpieczny dla wątku:

template<typename T, typename P>
class smart_ptr : protected P
{
public:
    // ... 
    smart_ptr(smart_ptr const& sp)
        :
        p(sp.p),
        refcount(sp.refcount)
    {
        P::add_ref(refcount);
    }
    // ...
private:
    T* p;
    int* refcount;
};

W kontekście wielowątkowym klient może użyć instancji szablonu inteligentnego wskaźnika z polityką, która realizuje bezpieczne dla wątku przyrosty i przyrosty licznika referencyjnego (Platforma Windows zakłada tutaj):

class mt_refcount_policy
{
protected:
    add_ref(int* refcount) { ::InterlockedIncrement(refcount); }
    release(int* refcount) { ::InterlockedDecrement(refcount); }
};

template<typename T>
using my_smart_ptr = smart_ptr<T, mt_refcount_policy>;
W środowisku jednowątkowym klient może utworzyć instancję szablonu inteligentnego wskaźnika za pomocą klasy policy, która po prostu zwiększa i zmniejsza wartość licznika.]}
class st_refcount_policy
{
protected:
    add_ref(int* refcount) { (*refcount)++; }
    release(int* refcount) { (*refcount)--; }
};

template<typename T>
using my_smart_ptr = smart_ptr<T, st_refcount_policy>;
W ten sposób projektant bibliotek dostarczył elastyczne rozwiązanie, które jest w stanie zaoferować najlepszy kompromis między wydajnością a bezpieczeństwem ("nie płacisz za to, czego nie używasz").
 22
Author: Andy Prowl,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-06 09:28:30

Jeśli używasz ModeT, IsReentrant i IsAsync do kontrolowania zachowania serwera, to jest to Polityka.

Alternatywnie, jeśli chcesz opisać cechy serwera innemu obiektowi, możesz zdefiniować klasę cech w następujący sposób:

template <typename ServerType>
class ServerTraits;

template<>
class ServerTraits<Server>
{
    enum { ModeT = SomeNamespace::MODE_NORMAL };
    static const bool IsReentrant = true;
    static const bool IsAsync = true;
}
 3
Author: Twisted Oracle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-17 23:11:20

Oto kilka przykładów do wyjaśnienia komentarza Alexa Chamberlaina:

Powszechnym przykładem klasy trait jest std:: iterator_traits. Załóżmy, że mamy jakiś szablon klasy C z funkcją member, która pobiera dwa Iteratory, iteruje nad wartościami i gromadzi wynik w jakiś sposób. Chcemy, aby strategia akumulacji również została zdefiniowana jako część szablonu, ale w tym celu wykorzystamy politykę, a nie cechę.

template <typename Iterator, typename AccumulationPolicy>
class C{
    void foo(Iterator begin, Iterator end){
        AccumulationPolicy::Accumulator accumulator;
        for(Iterator i = begin; i != end; ++i){
            std::iterator_traits<Iterator>::value_type value = *i;
            accumulator.add(value);
        }
    }
};

Polityka jest przekazywana do naszego szablonu klasy, natomiast cecha pochodzi z parametru szablonu. Więc to, co masz, jest bardziej podobne do polityki. Są sytuacje, w których cechy są bardziej odpowiednie i gdzie polityki są bardziej odpowiednie, a często ten sam efekt można osiągnąć przy użyciu jednej z metod prowadzących do pewnej debaty, która jest najbardziej wyrazista.

 1
Author: jmetcalfe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-04 09:44:57