Co oznacza template <unsigned int N> oznacza?

121

Deklarując szablon, jestem przyzwyczajony do posiadania tego rodzaju kodu:

template <class T>

Ale w tym pytaniu użyli:

template <unsigned int N>

Sprawdziłem, czy się kompiluje. Ale co to oznacza? Czy jest to parametr inny niż typ? A jeśli tak, jak możemy mieć szablon bez żadnego parametru typu?

Igor Oks
źródło

Odpowiedzi:

148

Jest całkowicie możliwe utworzenie szablonu klasy na podstawie liczby całkowitej zamiast typu. Możemy przypisać wartość szablonu do zmiennej lub w inny sposób manipulować nią w sposób, w jaki moglibyśmy to zrobić z dowolnym innym literałem całkowitoliczbowym:

unsigned int x = N;

W rzeczywistości możemy stworzyć algorytmy, które oceniają w czasie kompilacji (z Wikipedii ):

template <int N>
struct Factorial 
{
     enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}
maxaposteriori
źródło
11
Możesz także użyć type static constexpr intzamiast swojego enum. Tak więc Factorial<0>szablon miałby static constexpr int value = 1i template <int N> struct Factorialmoże miećstatic constexpr int value = N * Factorial<N - 1>::value;
bobobobo
@bobobobo odpowiedź na to pytanie pojawiła się przed C ++ 11 i constexpr.
Justin Meiners,
154

Tak, jest to parametr inny niż typ. Możesz mieć kilka rodzajów parametrów szablonu

  • Parametry typu.
    • Rodzaje
    • Szablony (tylko szablony klas i aliasów, bez funkcji i szablonów zmiennych)
  • Parametry inne niż typowe
    • Wskaźniki
    • Bibliografia
    • Wyrażenia stałe całkowe

To, co tam masz, jest ostatniego rodzaju. Jest to stała czasowa kompilacji (tak zwane wyrażenie stałe) i jest typu integer lub enumeration. Po sprawdzeniu tego w standardzie musiałem przenieść szablony klas w górę do sekcji typów - mimo że szablony nie są typami. Niemniej jednak w celu opisania tych rodzajów nazywa się je parametrami typu. Możesz mieć wskaźniki (a także wskaźniki składowe) i odniesienia do obiektów / funkcji, które mają zewnętrzne powiązania (te, do których można dowiązać z innych plików obiektowych i których adres jest unikalny w całym programie). Przykłady:

Parametr typu szablonu:

template<typename T>
struct Container {
    T t;
};

// pass type "long" as argument.
Container<long> test;

Parametr liczby całkowitej szablonu:

template<unsigned int S>
struct Vector {
    unsigned char bytes[S];
};

// pass 3 as argument.
Vector<3> test;

Parametr wskaźnika szablonu (przekazywanie wskaźnika do funkcji)

template<void (*F)()>
struct FunctionWrapper {
    static void call_it() { F(); }
};

// pass address of function do_it as argument.
void do_it() { }
FunctionWrapper<&do_it> test;

Parametr odwołania do szablonu (przekazywanie liczby całkowitej)

template<int &A>
struct SillyExample {
    static void do_it() { A = 10; }
};

// pass flag as argument
int flag;
SillyExample<flag> test;

Parametr szablonu szablonu.

template<template<typename T> class AllocatePolicy>
struct Pool {
    void allocate(size_t n) {
        int *p = AllocatePolicy<int>::allocate(n);
    }
};

// pass the template "allocator" as argument. 
template<typename T>
struct allocator { static T * allocate(size_t n) { return 0; } };
Pool<allocator> test;

Szablon bez parametrów nie jest możliwy. Ale szablon bez żadnego jawnego argumentu jest możliwy - ma domyślne argumenty:

template<unsigned int SIZE = 3>
struct Vector {
    unsigned char buffer[SIZE];
};

Vector<> test;

Składniowo, template<>jest zarezerwowany do oznaczania wyraźnej specjalizacji szablonu, zamiast szablonu bez parametrów:

template<>
struct Vector<3> {
    // alternative definition for SIZE == 3
};
Johannes Schaub - litb
źródło
Johannes, czy szablony są umieszczane w „typach”? Myślałem, że z jakich typów można zrobić, ale nie z samych typów?
sbi
@sbi zobacz wyjaśnienie: „Po sprawdzeniu tego w standardzie musiałem przenieść szablony klas w górę do sekcji typów - mimo że szablony nie są typami. Jednak w celu opisania tych rodzajów nazywa się je parametrami typu. ”. Tak mówi przypis 126 na 14.1 / 2. Jest to po prostu klasyfikacja, która ma na celu uczynienie parametrów innych niż typ czymś, co deklaruje wartość / odwołanie, a parametry typu są czymś, co deklaruje nazwę typu lub nazwę szablonu.
Johannes Schaub - litb
@ JohannesSchaub-litb, więc nie ma możliwości wpisania szablonu z, powiedzmy, std :: string? na przykład klasa template <std :: string S> z jakimś statycznym licznikiem w celu stworzenia unikalnego identyfikatora dla każdego innego ciągu? haszowanie łańcucha do int byłoby niestety jedynym sposobem, prawda?
relaxxx
1
Bardzo chciałbym, aby ta odpowiedź była zakończona obiektami składowymi klasy szablonu, tj. Szablon <nazwa typu C, nazwa typu R, nazwa typu P1, nazwa typu P2> struct mystruct <R (C :: *) (P1, P2)>
Johnny Pauling
Fragment kodu z SillyExamplenie może zostać skompilowany przez GCC 4.8.4. Pierwszy błąd to the value of ‘flag’ is not usable in a constant expression. Są też inne błędy
HEKTO
17

Tworzysz szablon swojej klasy na podstawie „unsigned int”.

Przykład:

template <unsigned int N>
class MyArray
{
    public:
    private:
        double    data[N]; // Use N as the size of the array
};

int main()
{
    MyArray<2>     a1;
    MyArray<2>     a2;

    MyArray<4>     b1;

    a1 = a2;  // OK The arrays are the same size.
    a1 = b1;  // FAIL because the size of the array is part of the
              //      template and thus the type, a1 and b1 are different types.
              //      Thus this is a COMPILE time failure.
 }
Martin York
źródło
15

Klasa szablonów jest jak makro, tylko dużo mniej zła.

Pomyśl o szablonie jak o makrze. Parametry szablonu są podstawiane do definicji klasy (lub funkcji), kiedy definiujesz klasę (lub funkcję) za pomocą szablonu.

Różnica polega na tym, że parametry mają „typy”, a przekazywane wartości są sprawdzane podczas kompilacji, podobnie jak parametry do funkcji. Prawidłowe typy to zwykłe typy w C ++, takie jak int i char. Tworząc instancję klasy szablonu, przekazujesz wartość określonego typu, aw nowej kopii definicji klasy szablonu ta wartość jest zastępowana w miejscu, w którym nazwa parametru występowała w oryginalnej definicji. Podobnie jak makro.

Możesz także użyć typów „ class” lub „ typename” jako parametrów (są one naprawdę takie same). W przypadku parametru jednego z tych typów można przekazać nazwę typu zamiast wartości. Tak jak poprzednio, wszędzie, gdzie nazwa parametru znajdowała się w definicji klasy szablonu, gdy tylko utworzysz nową instancję, stanie się dowolnym przekazanym typem. Jest to najczęstsze zastosowanie klasy szablonów; Każdy, kto wie cokolwiek o szablonach C ++, wie, jak to zrobić.

Rozważ ten przykładowy kod klasy szablonu:

#include <cstdio>
template <int I>
class foo
{
  void print()
  {
    printf("%i", I);
  }
};

int main()
{
  foo<26> f;
  f.print();
  return 0;
}

Funkcjonalnie jest taki sam jak ten kod wykorzystujący makra:

#include <cstdio>
#define MAKE_A_FOO(I) class foo_##I \
{ \
  void print() \
  { \
    printf("%i", I); \
  } \
};

MAKE_A_FOO(26)

int main()
{
  foo_26 f;
  f.print();
  return 0;
}

Oczywiście wersja szablonu jest miliard razy bezpieczniejsza i bardziej elastyczna.

Jonathan
źródło