• 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
261 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 (156,260 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 (349,960 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,718 wizyt
pytanie zadane 16 czerwca 2015 w C i C++ przez draghan VIP (106,230 p.)
0 głosów
1 odpowiedź 1,796 wizyt
pytanie zadane 13 listopada 2016 w C i C++ przez Bączal Nowicjusz (120 p.)
0 głosów
1 odpowiedź 358 wizyt

93,024 zapytań

141,986 odpowiedzi

321,291 komentarzy

62,371 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

Wprowadzenie do ITsec, tom 2

Można już zamawiać tom 2 książki "Wprowadzenie do bezpieczeństwa IT" - będzie to około 650 stron wiedzy o ITsec (17 rozdziałów, 14 autorów, kolorowy druk).

Planowana premiera: 30.09.2024, zaś planowana wysyłka nastąpi w drugim tygodniu października 2024.

Warto preorderować, tym bardziej, iż mamy dla Was kod: pasja (użyjcie go w koszyku), dzięki któremu uzyskamy dodatkowe 15% zniżki! Dziękujemy zaprzyjaźnionej ekipie Sekuraka za kod dla naszej Społeczności!

...