web-dev-qa-db-ja.com

template <unsigned int N>はどういう意味ですか?

テンプレートを宣言するとき、私はこの種のコードを持つことに慣れています:

template <class T>

しかし、 この質問では 、彼らは使用しました:

template <unsigned int N>

コンパイルすることを確認しました。しかし、それはどういう意味ですか?型のないパラメーターですか?もしそうなら、どのように型パラメータなしのテンプレートを持つことができますか?

106
Igor Oks

型ではなく整数でクラスをテンプレート化することは完全に可能です。テンプレート化された値を変数に割り当てることも、他の整数リテラルと同じように操作することもできます。

unsigned int x = N;

実際、コンパイル時に評価するアルゴリズムを作成できます( 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
}
135
maxaposteriori

はい、それは非型パラメーターです。数種類のテンプレートパラメータを使用できます

  • 型パラメーター。
    • タイプ
    • テンプレート(クラスとエイリアステンプレートのみ、関数または変数テンプレートはなし)
  • 非型パラメーター
    • ポインタ
    • 参照資料
    • 整数定数式

そこにあるのは最後の種類です。これはコンパイル時定数(いわゆる定数式)であり、整数型または列挙型です。標準で調べた後、テンプレートは型ではありませんが、クラステンプレートを型セクションに移動する必要がありました。しかし、それでもこれらの種類を記述するために、型パラメーターと呼ばれます。ポインター(およびメンバーポインター)と、外部リンケージ(他のオブジェクトファイルからリンクでき、プログラム全体でアドレスが一意であるもの)を持つオブジェクト/関数への参照を持つことができます。例:

テンプレートタイプパラメータ:

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

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

テンプレート整数パラメーター:

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

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

テンプレートポインターパラメーター(関数へのポインターを渡す)

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;

テンプレート参照パラメーター(整数を渡す)

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

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

テンプレートテンプレートパラメータ。

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<unsigned int SIZE = 3>
struct Vector {
    unsigned char buffer[SIZE];
};

Vector<> test;

構文的には、template<>は、パラメータなしのテンプレートではなく、明示的なテンプレートの特殊化をマークするために予約されています。

template<>
struct Vector<3> {
    // alternative definition for SIZE == 3
};
141

「unsigned int」に基づいてクラスをテンプレート化します。

例:

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.
 }
15
Martin York

テンプレートクラスはマクロのようなもので、悪さはまったくありません。

テンプレートをマクロと考えてください。テンプレートを使用してクラス(または関数)を定義すると、テンプレートのパラメーターはクラス(または関数)定義に置き換えられます。

違いは、パラメーターには「タイプ」があり、渡される値は、関数へのパラメーターのようにコンパイル中にチェックされることです。有効な型は、intやcharなどの通常のC++型です。テンプレートクラスをインスタンス化するときは、指定した型の値を渡します。テンプレートクラス定義の新しいコピーでは、この値は、パラメーター名が元の定義にあった場所で置き換えられます。マクロのように。

パラメーターに「class」または「typename」タイプを使用することもできます(実際は同じです)。これらのタイプのいずれかのパラメーターを使用すると、値の代わりにタイプ名を渡すことができます。前と同じように、新しいインスタンスを作成するとすぐに、パラメータ名がテンプレートクラス定義内にあったすべての場所が、渡す型になります。これは、テンプレートクラスの最も一般的な使用法です。 C++テンプレートについて何かを知っている人は誰でもこれを行う方法を知っています。

このテンプレートクラスのサンプルコードを検討してください。

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

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

機能的には、このマクロを使用するコードと同じです。

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

もちろん、テンプレートバージョンは10億倍安全で柔軟です。

13
Jonathan