Apa arti template <unsigned int N>?

121

Saat mendeklarasikan template, saya terbiasa memiliki kode semacam ini:

template <class T>

Namun dalam pertanyaan ini , mereka menggunakan:

template <unsigned int N>

Saya memeriksa bahwa itu terkompilasi. Tapi apa artinya? Apakah ini parameter non-tipe? Dan jika demikian, bagaimana kita bisa memiliki template tanpa parameter tipe?

Igor Oks
sumber

Jawaban:

148

Sangat mungkin untuk membuat template kelas pada integer daripada tipe. Kita dapat menetapkan nilai templated ke variabel, atau memanipulasinya dengan cara yang mungkin kita lakukan dengan literal integer lainnya:

unsigned int x = N;

Faktanya, kita dapat membuat algoritma yang mengevaluasi pada waktu kompilasi (dari Wikipedia ):

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.dll
sumber
11
Anda juga dapat menggunakan tipe, static constexpr intbukan Anda enum. Jadi Factorial<0>templatnya akan memiliki static constexpr int value = 1, dan template <int N> struct Factorialdapat memilikistatic constexpr int value = N * Factorial<N - 1>::value;
bobobobo
@bobobo ini dijawab sebelum C ++ 11 dan constexpr.
Justin Meiners
154

Ya, ini adalah parameter non-tipe. Anda dapat memiliki beberapa jenis parameter template

  • Jenis Parameter.
    • Jenis
    • Template (hanya kelas dan template alias, tanpa fungsi atau template variabel)
  • Parameter Non-tipe
    • Pointer
    • Referensi
    • Ekspresi konstanta integral

Apa yang Anda miliki di sana adalah jenis yang terakhir. Ini adalah konstanta waktu kompilasi (disebut ekspresi konstan) dan berjenis integer atau enumerasi. Setelah mencarinya di standar, saya harus memindahkan templat kelas ke bagian tipe - meskipun templat bukan tipe. Tetapi mereka disebut tipe-parameter untuk tujuan mendeskripsikan jenis-jenis itu. Anda dapat memiliki pointer (dan juga pointer anggota) dan referensi ke objek / fungsi yang memiliki hubungan eksternal (yang dapat ditautkan dari file objek lain dan yang alamatnya unik di seluruh program). Contoh:

Parameter jenis template:

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

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

Parameter bilangan bulat template:

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

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

Parameter penunjuk template (meneruskan penunjuk ke suatu fungsi)

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;

Parameter referensi template (meneruskan integer)

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

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

Parameter template template.

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;

Template tanpa parameter apa pun tidak dimungkinkan. Tetapi templat tanpa argumen eksplisit apa pun dimungkinkan - itu memiliki argumen default:

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

Vector<> test;

Secara sintaksis, template<>dicadangkan untuk menandai spesialisasi template eksplisit, bukan template tanpa parameter:

template<>
struct Vector<3> {
    // alternative definition for SIZE == 3
};
Johannes Schaub - litb
sumber
Johannes, apakah template dimasukkan ke dalam "tipe"? Saya pikir mereka adalah tipe apa yang bisa dibuat, tetapi bukan tipe itu sendiri?
sbi
@sbi melihat penjelasannya: "Setelah mencarinya di standar, saya harus memindahkan template kelas ke bagian tipe - meskipun template bukan tipe. Tetapi mereka disebut parameter tipe untuk tujuan mendeskripsikan jenis-jenis itu. ". Catatan kaki 126 di 14.1 / 2 mengatakan demikian. Ini hanya klasifikasi yang dibuat untuk membuat parameter non-tipe menjadi sesuatu yang mendeklarasikan nilai / referensi dan tipe-parameter menjadi sesuatu yang mendeklarasikan nama tipe atau nama template.
Johannes Schaub - litb
@ JohannesSchaub-litb jadi tidak ada cara untuk mengetik template dengan katakanlah std :: string? seperti template kelas <std :: string S> dengan beberapa penghitung statis di dalamnya untuk membuat id unik untuk setiap string yang berbeda? hashing string to int akan menjadi satu-satunya cara sayangnya kan?
relaxxx
1
Saya ingin melihat jawaban ini dilengkapi dengan objek anggota kelas template, yaitu template <nama jenis C, nama jenis R, nama jenis P1, nama jenis P2> struct mystruct <R (C :: *) (P1, P2)>
Johnny Pauling
Bagian kode dengan SillyExampletidak dapat dikompilasi oleh GCC 4.8.4. Kesalahan pertama adalah the value of ‘flag’ is not usable in a constant expression. Ada kesalahan lain juga
HEKTO
17

Anda membuat template kelas Anda berdasarkan 'unsigned int'.

Contoh:

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
sumber
15

Kelas template seperti makro, hanya saja tidak terlalu jahat.

Pikirkan templat sebagai makro. Parameter pada template diganti menjadi definisi kelas (atau fungsi), saat Anda mendefinisikan kelas (atau fungsi) menggunakan template.

Perbedaannya adalah bahwa parameter memiliki "jenis" dan nilai yang diteruskan diperiksa selama kompilasi, seperti parameter ke fungsi. Tipe yang valid adalah tipe C ++ reguler Anda, seperti int dan char. Saat Anda membuat instance kelas template, Anda meneruskan nilai dari tipe yang Anda tentukan, dan dalam salinan baru definisi kelas template, nilai ini diganti di mana pun nama parameter berada dalam definisi aslinya. Sama seperti makro.

Anda juga dapat menggunakan jenis " class" atau " typename" untuk parameter (keduanya benar-benar sama). Dengan parameter dari salah satu jenis ini, Anda dapat meneruskan nama jenis alih-alih nilai. Sama seperti sebelumnya, di mana pun nama parameter berada di definisi kelas template, segera setelah Anda membuat instance baru, menjadi jenis apa pun yang Anda berikan. Ini adalah penggunaan paling umum untuk kelas template; Semua orang yang tahu tentang template C ++ tahu bagaimana melakukan ini.

Pertimbangkan kode contoh kelas template ini:

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

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

Fungsinya sama dengan kode penggunaan makro ini:

#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;
}

Tentu saja, versi templatnya satu miliar kali lebih aman dan lebih fleksibel.

Jonathan
sumber