0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C++ templateが怖い (4)

0
Last updated at Posted at 2026-02-14

テンプレートパラメータとしてのリテラル型クラス(復帰版)

文字列リテラルや数値リテラルという言葉がありますね。
これは、ソースコードに書いた文字列定数や{整数/浮動小数点/文字}定数の文字列のことです。

"大四喜字一色十枚爆弾" ←文字列リテラル
42 ←整数リテラル
6e+23 ←浮動小数点リテラル
'a' ←文字リテラル

リテラル型というのはこのようにコンパイル時定数式が書ける型のことです。

要するに

  • 整数型
  • 浮動小数点型
  • 列挙型
  • ポインタ型

というスカラー型はすべてこれに該当します。
そして、リテラル型クラス/構造体とは、そのまんま(literally)リテラル型のクラス/構造体のことです。(ここダジャレですよ!)

クラス/構造体がリテラル型になる要件は以下のとおりです。

  • constexpr なコンストラクタを持つ
  • デストラクタが trivial または constexpr
  • 非static データメンバがすべてリテラル型
  • 仮想関数を持たない
    (※細かい条件は規格改訂で変わっています)

この要件を満たしているクラス/構造体はコンパイル時定数式を書くことができて、それをテンプレートパラメータにすることと、コンパイル時暗算にも使用することができるというわけです。
constexpr はコンパイル時評価のための通行証のようなものです。リテラル型クラスという概念ができたことでコンパイル時評価の世界にクラス/構造体も入ることが可能になり、定数式を書けることでテンプレートパラメータにできるようになったのです。

コードは以下のようになります。

#include <iostream>

struct Point
{
    int x;
    int y;

    constexpr Point(int x_, int y_) : x(x_), y(y_) {}
};

// C++20: クラス型の非型テンプレート引数
template <Point P>
struct Printer
{
    static void print()
    {
        std::cout << "(" << P.x << ", " << P.y << ")\n";
    }
};

int main()
{
    Printer<Point{3, 4}>::print();
}

テンプレートパラメータとしてのテンプレート

最後にテンプレートをパラメータ化したテンプレートも取り上げましょう。

#include <iostream>
#include <vector>
#include <list>

template <template <typename> class Container>
struct Wrapper {
    Container<int> data;
    void add(int v) {
        data.push_back(v);
    }
};
int main() {
    Wrapper<std::vector> w1;
    Wrapper<std::list>   w2;
    w1.add(10);
    w2.add(20);
}

std::vectorstd::list もパラメータにできるのです。

template <template <typename> class Container>

という部分がすごいですね。

auto が使える

非型、非テンプレートなテンプレートパラメータには int やら int* やらの特定の型を書けますが、ここは auto にすることも出来ます。

template <auto V>
void print_value() {
    // V の型は渡された瞬間に決まっている
    std::cout << V << std::endl;
}

int main() {
    print_value<100>();        // V は int 型の 100
    print_value<'A'>();        // V は char 型の 'A'
    print_value<3.14>();       // V は double 型の 3.14 (C++20)
    
    static int x = 42;
    print_value<&x>();         // V は int* 型の &x
}

テンプレートのインスタンス化とは

テンプレートはひな型です。
そこに型、テンプレート、定数を与えることで具体的なクラスや関数が生成されます。
この具体的なクラスや関数を生成することをインスタンス化といいます。

// これはただのひな型でしかない。
template <typename T>
struct ANYTHING {
    T member;
};

int main()
{
    ANYTHING<int> a; // これでインスタンス化される
    ANYTHING<double>  b; // また別のインスタンスがつくられる
}

通常はテンプレートはヘッダファイルに書き、そのテンプレートを利用するソース・ファイルから、それを include します。

そうすると複数の翻訳単位(ソースファイル)で同じテンプレートのインスタンスが作られることもあります。
重複しているものはリンク時に破棄されます。
重複しているかどうかというのは何で判断されるのでしょうか?

それはシンボル名です。

(つづく)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?