• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

[C++11] Jak zabronić szablonowi klasy "współpracowania" z konkretnymi typami?

VPS Starter Arubacloud
0 głosów
246 wizyt
pytanie zadane 22 marca 2017 w C i C++ przez Arkadiusz Sikorski Pasjonat (20,160 p.)

[C++11]

Interesowałem się ostatnio szablonami klas i zastanawiałem się czy można zabraniać używania konkretnych typów z szablonem klasy (lub pozwolić tylko konkretnym typom na współpracę z danym szablonem). Na początek interesuje mnie wykorzystywanie typów wbudowanych - przykładowo chciałbym pozwolić, żeby szablon mógł "współpracować" ze wszystkimi (oczywiście pasującymi składniowo) typami, a z typem unsigned int czy char już nie. Albo z drugiej strony: chciałbym, żeby szablon klasy akceptował tylko jakieś wybrane przeze mnie typy, na przykład wyłącznie double i float.

Czy ktoś mógłby w miarę przystępny sposób wyjaśnić to zagadnienie i podać jakiś mały przykład? W Internecie widziałem coś o statycznych asercjach, ale szczerze mówiąc, niewiele z tego zrozumiałem. 

2 odpowiedzi

+3 głosów
odpowiedź 22 marca 2017 przez mokrowski Mędrzec (155,460 p.)

Wiele konstrukcji zbudujesz z zastosowaniem static_assert. Kilka z nich uda Ci się zbudować z użyciem typowego if'a i metafunkcji z nagłówka <type_traits> oraz bazowaniu na optymalizacji kompilatora (usunie martwe gałęzie kodu). Kilka interesujących właściwości osiągalnych z samych szablonów masz także w przykładzie. Temat jest jednak zbyt szeroki by opisać go w 1 poście. Nie upieram się także że każdy z tych przykładów jest "najbardziej prosty z prostych" lub mogę twierdzić że "zawsze rób w ten sposób". Zacznij oczywiście od bardzo prostych szablonów.. :-)

#include <iostream>
#include <type_traits>

// Funkcje... 
// 
// Przypadek: "Wszystkie typy posiadające domyślny konstruktor mogą,
// ale jeden (lub kilka) nie..."
//

// Szablon funkcji wyświetlającej dla jakich typów się 
// skonkretyzował.
template<typename T>
T function1(T value) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return T{value};
}

// Jawne usunięcie konkretyzacji dla typu double
template<>
double function1<double>(double) = delete;
//
// XXX: Z ciekawostek delete nie powiedzie się dla typu void*. Tak powinno
// być ze względu na obsługę "surowych wskaźników".
//
// Przypadek: "Żaden typ nie może być poddany konkretyzacji
// ale są takie które mogą."
//

// Ogólny szablon nie podda się konkretyzacji.
// Jeśli chcesz możesz użyć static_assert .. 
template<typename T>
T function2(T value);

// Konkretyzacja dla tego szablonu się powiedzie.
template<>
double function2<double>(double value) {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
    return double{value};
}

// Dla struktur
//
// Przypadek: "Wszyscy mogą tylko kilka nie.. "
//
template<typename T>
struct Class1 {
};

// To się nie uda...
// XXX: Tu także jeśli to konieczne można użyć static_assert
template<>
struct Class1<int>;

// 
// Przypadek: "Nikt nie może tylko kilka tak... "
//
// Zabronienie poprzez usunięcie domyślnego konstruktora...
// To samo można wykonać dla konstruktorów/operatorów przypisania 
// które są właściwe dla danej klasy.
template<typename T>
struct Class2 {
    Class2() = delete;
};

template<>
struct Class2<double> {
};

template<>
struct Class2<int[]> {
};


int main() {
    auto z = 123.4f;
    function1(z);
    // To się nie powiedzie...
    //function1(0.0);
    
    // To się nie powiedzie...
    //function2(424.4f);
    // .. ale to owszem...
    function2(0.1);

    // To się nie powiedzie..
    //auto c1 = Class1<int>();
    // A to tak.
    [[maybe_unused]] auto c1 = Class1<double>();

    // To się nie uda..
    //auto c2 = Class2<float>();
    // A to tak..
    [[maybe_unused]] auto c2 = Class2<double>();
    // XXX: Atrybut [[maybe_unused]] wchodzi w C++17. Ja go tu użyłem by nie mieć 
    // ostrzegających zgłoszeń kompilatora o nieużywanych zmiennych.
}

 

komentarz 24 marca 2017 przez Arkadiusz Sikorski Pasjonat (20,160 p.)
Dzięki wielkie za dużą garść solidnych przykładów!
+2 głosów
odpowiedź 22 marca 2017 przez adrian17 Ekspert (344,100 p.)

Absolutnie najprościej? Zdecydowanie static_assert.

http://en.cppreference.com/w/cpp/language/static_assert

template <class T>
struct Stuff
{
    static_assert(std::is_floating_point<T>::value, "Only floating point types");
};

 

Podobne pytania

+3 głosów
1 odpowiedź 3,535 wizyt
pytanie zadane 16 czerwca 2015 w C i C++ przez draghan VIP (106,230 p.)
0 głosów
1 odpowiedź 1,696 wizyt
pytanie zadane 13 listopada 2016 w C i C++ przez Bączal Nowicjusz (120 p.)
0 głosów
1 odpowiedź 274 wizyt

92,454 zapytań

141,263 odpowiedzi

319,099 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...