ノンタイプテンプレート 解答ページ | Programming Place Plus 新C++編

トップページ新C++編ノンタイプテンプレート

このページの概要 🔗

このページは、練習問題の解答例や解説のページです。



解答・解説 🔗

問題1 (確認★) 🔗

次のうち、ノンタイプテンプレート仮引数の使い方に問題があるものをすべて選んでください。

  1. template <typename T, int N>
  2. template <int N1, int N2, int N3 = 0>
  3. template <int N, double F>
  4. template <int* P>
  5. template <char S[128]>
  6. template <std::string S>


1番は問題ありません。型を指定するテンプレート仮引数と、値を指定するノンタイプテンプレート仮引数を混ぜることが可能です(本編解説)。

2番は問題ありません。ノンタイプテンプレート仮引数を複数使うことはもちろんのこと、デフォルトテンプレート実引数を使うこともできます。

3番は、C++14 においては問題があります。ノンタイプテンプレート仮引数に浮動小数点型を使うことはできません(本編解説)。C++20 からは使用可能になっており、問題なくなりました。

4番は問題ありません。ノンタイプテンプレート仮引数にポインタ型を使用することができます。

5番は問題ありませんが、ノンタイプテンプレート仮引数に配列型を指定した場合、ポインタ型に置き換えられます。そのためこの指定は char* を意味します。

6番は問題があります。ノンタイプテンプレート仮引数にクラス型を使うことはできません。


ポインタ型のノンタイプテンプレート仮引数に対するノンタイプテンプレート実引数には、静的ストレージ期間で、外部結合または内部結合のオブジェクトのメモリアドレスを &x のかたちで指定しなければなりません(本編解説)。

#include <iostream>

template <int* P>
class C {
};

int n1 {10};         // 静的ストレージ期間で外部結合
static int n2 {10};  // 静的ストレージ期間で内部結合

int main()
{
    C<&n1> c1 {};  // OK
    C<&n2> c2 {};  // OK
    //C<&n1 + 0> c3 {};  // これは受け付けない
}

Visual Studio 2015 では、“内部リンケージがあるオブジェクトを含む表現を、非型引数として使用することはできません。” という明らかに規格と合わない理由で、c2 がコンパイルエラーになります。Visual Studio 2017 以降ではエラーになりません。

一方で、たとえば関数内で宣言した変数は無結合なため、そのメモリアドレスを渡すことはできません。

#include <iostream>

template <int* P>
class C {
};

int main()
{
    static int n {10};  // 静的ストレージ期間を持つが無結合

    C<&n> c {};      // エラー
}

問題2 (基本★) 🔗

size_of_array関数テンプレートの考え方を利用して、配列の末尾の要素を返す関数テンプレートを実装してください。


size_of_array関数テンプレートは次のように実装されていました。

template <typename T, std::size_t SIZE>
inline std::size_t size_of_array(const T (&array)[SIZE])
{
    return SIZE;
}

ノンタイプテンプレート仮引数 SIZE に、(通常、テンプレート実引数推論によって)配列の要素数が渡されることを利用したものです(本編解説)。SIZE に要素数があるなら、SIZE - 1 したものが末尾の要素の添字として使えます。

したがって、以下のように実装できます。

#include <iostream>

template <typename T, std::size_t SIZE>
inline T get_array_tail(const T (&array)[SIZE])
{
    return array[SIZE - 1];
}

int main()
{
    int array1[] {3, 5};
    int array2[] {5, 7, 6, 3, 6};

    std::cout << get_array_tail(array1) << "\n";
    std::cout << get_array_tail(array2) << "\n";
}

実行結果:

5
6

問題3 (応用★★) 🔗

std::array の要素を逆順になるように入れ替えるプログラムを作成してください。


std::array にはイテレータがあります。一部の例外はありえますが、基本的にイテレータを用いる関数のほとんどは使えます。そのため、std::reverse関数[1]を使うのが簡単です。

#include <algorithm>
#include <array>
#include <iostream>

int main()
{
    std::array<int, 10> a {-3, -1, 0, 2, 4, 5, 5, 7, 9, 10};

    std::reverse(std::begin(a), std::end(a));

    for (const auto& e : a) {
        std::cout << e << "\n";
    }
}

実行結果:

10
9
7
5
5
4
2
0
-1
-3

問題4 (応用★★) 🔗

std::bitset の下位nビットを出力する関数テンプレートを作成してください。


指定された n の値が、std::bitset のビット数を超える場合も考慮しておきましょう。たとえば以下のように実装できます。

#include <algorithm>
#include <bitset>
#include <iostream>

template <std::size_t N>
void print_lower_bits(const std::bitset<N>& bs, std::size_t bitnum)
{
    const int index {static_cast<int>(std::min(bitnum, N) - 1)};

    for (int i {index}; i >= 0; --i) {
        std::cout << bs[i];
    }
    std::cout << "\n";
}

int main()
{
    std::bitset<16> bs {0b1100'1010'0011'1110};
    print_lower_bits(bs, 4);
    print_lower_bits(bs, 8);
    print_lower_bits(bs, 32);
}

実行結果:

1110
00111110
1100101000111110


参考リンク 🔗



更新履歴 🔗




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る