情報学部 菅沼ホーム 全体目次 演習解答例 付録 索引

第14章 テンプレート

  1. 14.1 関数テンプレート
    1. (プログラム例 14.1 ) 関数テンプレート
  2. 14.2 クラステンプレート
    1. (プログラム例 14.2 ) クラステンプレート
  3. 14.3 C++ 標準ライブラリ
    1. 14.3.1 コンテナ
    2. 14.3.2 イテレータ(反復子)
      1. (プログラム例 14.3 ) イテレータ
      2. (プログラム例 14.4 ) 挿入イテレータ
      3. (プログラム例 14.5 ) ストリームイテレータ
    3. 14.3.3 アルゴリズム等
    4. 14.3.4 関数オブジェクト
      1. (プログラム例 14.6 ) map における関数オブジェクトの使用
      2. (プログラム例 14.7 ) 関数オブジェクトの作成
  4. 14.4 ラムダ式C++11
    1. (プログラム例 14.8 ) ラムダ式
    演習問題14

14.1 関数テンプレート

  例えば,様々な型の 2 つの値の大きさを比べ,小さい方を返す関数について考えてみます.C++ の場合,関数名が同じであっても,引数の型や数が異なれば異なる関数とみなされますので(関数名のオーバーロード),型毎に同じ名前の関数を定義するという方法によって実現することが可能です.しかし,次の形式で書かれる関数テンプレートを使用することによって,1 つの関数の定義だけですますことも可能です.
template <テンプレート引数宣言> 関数宣言または定義		
  テンプレート引数は関数宣言や定義の中で型名として使用でき,この型が任意の型に変換されます.例えば,次の例における関数 min_t では,型 cl の部分に double や int や Test が入ります.この例のように,クラス型の変数に対しても同じ関数を使用できますが,そのクラスのオブジェクトの大きさを比較する演算子「 < 」に対するオーバーロードが定義されていなければ成りません.また,この例の場合,関数の 2 つの引数は同じ型 cl ですので,異なった型との比較はエラーになります.なお,関数 min_o は,関数名のオーバーロード機能を利用した場合に対する例です.

(プログラム例 14.1 ) 関数テンプレート

/****************************/
/* 関数テンプレート         */
/*      coded by Y.Suganuma */
/****************************/
#include <iostream>
#include <string.h>

using namespace std;

/**************************/
/* クラスTestに関する定義 */
/**************************/
class Test {
	public:
		char *str;
		int n;
		Test () {}
		Test (char *str1, int n1) {
			str = new char [strlen(str1)+1];
			strcpy(str, str1);
			n = n1;
		}
		bool operator< (Test &t)   // < に対するオーバーロード
		{
			return strcmp(str, t.str) < 0 ? true : false;
		}
};

/****************************************/
/* 最小値を返す関数(関数テンプレート) */
/*      a,b : 比較するデータ            */
/****************************************/
template <class cl> cl min_t(cl a, cl b) {
	return a < b ? a : b;
}

/**********************************************/
/* 最小値を返す関数(関数名のオーバーロード) */
/*      a,b : 比較するデータ                  */
/**********************************************/
int min_o(int a, int b) {
	return a < b ? a : b;
}

double min_o(double a, double b) {
	return a < b ? a : b;
}

Test min_o(Test a, Test b) {
	return strcmp(a.str, b.str) < 0 ? a : b;
}

/******************/
/* mainプログラム */
/******************/
int main()
{
			// 関数テンプレート
	int i = 0, j = 1;
	double x = 10.0, y = 5.5;
	Test a("xyz",1), b("abc", 2), c = min_t(a, b);
	cout << "テンプレート:int " << min_t(i, j) << " double " << min_t(x, y) << " クラス Test (" << c.str << "," << c.n << ")\n";
			// 関数名のオーバーロード
	c = min_o(a, b);
	cout << "オーバーロード:int " << min_o(i, j) << " double " << min_o(x, y) << " クラス Test (" << c.str << "," << c.n << ")\n";

	return 0;
}
		
  このプログラムを実行すると,以下に示すような結果が得られます.
テンプレート:int 0 double 5.5 クラス Test (abc,2)
オーバーロード:int 0 double 5.5 クラス Test (abc,2)		

14.2 クラステンプレート

  クラスに対しても,関数と同様,以下のような形式でクラステンプレートを定義できます.
template <テンプレート引数宣言> class クラス名 {
	・・・・・
};		
  ただし,コンストラクタ,デストラクタ,及び,メンバー関数の本体をクラス定義の外側に記述する場合は,関数テンプレートによって記述する必要があります.したがって,以下に示すような記述方法になります.
		// コンストラクタ(デストラクタも同様)
template <テンプレート引数宣言> クラス名 <テンプレート引数> :: クラス名 (引数)
{
	・・・・・
};
		// メンバー関数
template <テンプレート引数宣言> クラス名 <テンプレート引数> :: メンバー関数名 (引数)
{
	・・・・・
};		
  次の例では,クラステンプレートを使用して,任意のサイズ(この例では,3 )の様々な型の 1 次元配列を確保するクラス Vector を定義しています.

(プログラム例14.2) クラステンプレート

/******************************/
/* クラステンプレート         */
/*      coded by Y.Suganuma   */
/******************************/
#include <iostream>
using namespace std;

/**************************/
/* クラスTestに関する定義 */
/**************************/
class Test {
	public:
		char *str;
		int n;
		Test () {}
		Test (char *str1, int n1) {
			str = new char [strlen(str1)+1];
			strcpy(str, str1);
			n = n1;
		}
};

/**********************/
/* クラスVectorの定義 */
/**********************/
template <class Tp, int sz> class Vector {
	public:
		Tp *p;
		int size;
					// コンストラクタ
		Vector()
		{
			size = sz;
			p    = new Tp [size];
		}
					// 追加
		bool add(int k, Tp v)
		{
			bool res = true;
			if (k < 0 || k > size-1) {
				cout << "      要素番号が不適当です\n";
				res = false;
			}
			else
				p[k] = v;
			return res;
		}
};

/********************************************/
/* コンストラクタ(クラスの外で記述する場合) */
/********************************************/
/*
template <class Tp, int sz> Vector <Tp, sz>::Vector()
{
	size = sz;
	p    = new Tp [size];
}
*/

/******************************************/
/* データの追加(クラスの外で記述する場合) */
/*      k : 追加する要素番号              */
/******************************************/
/*
template <class Tp, int sz> bool Vector <Tp, sz>::add(int k, Tp v)
{
	bool res = true;
	if (k < 0 || k > size-1) {
		cout << "      要素番号が不適当です\n";
		res = false;
	}
	else
		p[k] = v;
	return res;
}
*/

/************/
/* main関数 */
/************/
int main()
{
	int k, ei, sw;
	cout << "int(0) or Test(1) ? ";
	cin >> sw;
/*
	 整数
*/
	if (sw == 0) {
		Vector <int, 3> iv;
		cout << "*整数ベクトル*\n";
		cout << "   要素番号は ";
		cin >> k;
		while (k >= 0) {
			cout << "   要素は ";
			cin >> ei;
			if (iv.add(k, ei))
				cout << "      " << iv.p[k] << " を追加\n";
			cout << "   要素番号は ";
			cin >> k;
		}
	}
/*
	 クラスTestのオブジェクト
*/
	else {
		Vector <Test, 3> iv;
		cout << "*クラスTestのオブジェクト*\n";
		cout << "   要素番号は ";
		cin >> k;
		while (k >= 0) {
			char str[10];
			cout << "   要素(文字列と値)は ";
			cin >> str >> ei;
			Test t(str, ei);
			if (iv.add(k, t))
				cout << "      (" << iv.p[k].str << "," << iv.p[k].n << ") を追加\n";
			cout << "   要素番号は ";
			cin >> k;
		}
	}

	return 0;
}
		
  このプログラムを実行すると,以下に示すような結果が得られます.
int(0) or Test(1) ? 0   // int オブジェクトの場合
*整数ベクトル*
   要素番号は 0
   要素は 10
      10 を追加
   要素番号は 1
   要素は 20
      20 を追加
   要素番号は -1

int(0) or Test(1) ? 1   // Test オブジェクトの場合
*クラスTestのオブジェクト*
   要素番号は 0
   要素(文字列と値)は abc 10
      (abc,10) を追加
   要素番号は 2
   要素(文字列と値)は xyz -5
      (xyz,-5) を追加
   要素番号は -1
		

14.3 C++ 標準ライブラリ

  C に printf,sqrt など,多くの標準関数が存在するように,C++ にもクラスを使用した多くの標準ライブラリが存在します.その内,STL ( Standard Template Library ) は,テンプレートの機能を利用した要素の集まりであり,コンテナイテレータアルゴリズム関数オブジェクトアロケータなどから構成されています.アロケータは,コンテナのメモリ割り当てを管理するためのものであり,デフォルトのアロケータは allocator クラスのオブジェクトです.アプリケーション独自のアロケータを定義することも可能ですが,ほとんどの場合デフォルトのアロケータで間に合いますので,アロケータに関する説明は省略します.今まで使用してきた cin や cout も標準ライブラリの一種ですが,これらについては,入出力クラスを参照してください.また,コンテナ及びアルゴリズムの使用例については,付録を参照してください.

14.3.1 コンテナ

  オブジェクト( int や double のような基本データ型も可能)を入れるためのデータ構造です.配列もコンテナの一種といえますが,その構造は固定されています.また,領域の確保や解放についても,自分で責任を持って行わなければなりません.そのような煩わしさを避けるため,C++ には,動的に領域の確保や解放を行ってくれる様々な構造を持ったコンテナが用意されています.

array 固定長のオブジェクトを保持するコンテナであり,各要素は連続して格納されます.
deque deque は double-ended queue (二重終端キュー)の頭文字をとったものであり,シーケンスコンテナの一種です.vector と同様の機能を提供しますが,vector とは異なり,連続した位置のストレージに全ての要素を持つことを保証していません.
forward_list 単方向リストを保持します.
list 双方向リストを保持します.
map 連想配列,つまり,添え字として数値以外(キー)を使用してデータを参照できる配列を保持します.set と同様,データは自動的にソートされます.
multimap 同じキーを持つデータ(キーの重複)を許す map です.
multiset 同じ値を持つ要素許す set です.
priority_queue 優先順位付き待ち行列を保持します.要素を追加すると,優先順位に従いソートされます(デフォルトでは降順).STL コンテナの一部を利用して,特別な機能を持たせたクラスであり,コンテナアダプタと呼ばれています.
queue 待ち行列を保持します.STL コンテナの一部を利用して,特別な機能(コンテナの一方から要素が挿入され,反対側から要素を取り出す)を持たせたクラスであり,コンテナアダプタと呼ばれています.
set 要素を自動的にソートして保持し,要素自身をキーとした探索が可能です.map と似ていますが,map ではキーとデータを別々に保持します.
stack スタックを保持します.STL コンテナの一部を利用して,特別な機能を持たせたクラスであり,コンテナアダプタと呼ばれています.
unordered_map 標準の配列や vector と異なり,コンテナ内の要素へのアクセスは絶対的な位置(添え字)によるのではなくキーによります.コンテナ内の各要素は,キーのハッシュ値に基づきハッシュテーブルに格納されるため,決められた順序で並んでいるわけではありません.キーとそれに対応する値がペアとなった要素を持ち,かつ,同一のキー値を格納することはできません.
unordered_multimap 標準の配列や vector と異なり,コンテナ内の要素へのアクセスは絶対的な位置(添え字)によるのではなくキーによります.コンテナ内の各要素は,キーのハッシュ値に基づきハッシュテーブルに格納されるため,決められた順序で並んでいるわけではありません.キーとそれに対応する値がペアとなった要素を持ち,かつ,同一のキー値を格納することができます.
unordered_multiset 標準の配列や vector とは異なり,コンテナ内の要素へのアクセスは絶対的な位置(添え字)によるのではなく、キーによります.コンテナ内の各要素は,キーのハッシュ値に基づきハッシュテーブルに格納されるため,決められた順序で並んでいるわけではありません.キーそのものが要素でもあり,かつ,同一のキー値を格納することができます.
unordered_set 標準の配列や vector とは異なり,コンテナ内の要素へのアクセスは絶対的な位置(添え字)によるのではなく、キーによります.コンテナ内の各要素は,キーのハッシュ値に基づきハッシュテーブルに格納されるため,決められた順序で並んでいるわけではありません.キーそのものが要素でもあり,かつ,同一のキー値を格納することはできません.
vector 動的配列を保持します.C の配列と互換性を持ち,データ領域の連続性が保証されています.通常の動的配列の場合は,宣言時に大きさを宣言しなければなりませんが,vector の場合は必要ありません.また,要素数を途中で変更することも可能です.

14.3.2 イテレータ(反復子)

  通常の配列であれば,添え字やポインタによって簡単に各要素を参照できます.しかし,コンテナのように,その構造が複雑な場合は,それほど簡単ではありません.そこで,それを簡単にしたものがイテレータです.たとえば,it をイテレータとすると,ポインタと同じように,it++ で次の要素へ移動でき,また,*it によってその内容を参照できます.イテレータには,次の表に示すようなものが存在します.なお,イテレータを使用するためには,<iterator> ヘッダが必要です.

ランダムアクセスイテレータ 各要素にランダムにアクセスでき,かつ,要素の参照と設定が可能
双方向イテレータ 前方及び後方に移動でき,かつ,要素の参照と設定が可能
前方イテレータ 前方だけに移動でき,かつ,要素の参照と設定が可能
入力イテレータ 前方だけに移動でき,かつ,要素の設定が可能
出力イテレータ 前方だけに移動でき,かつ,要素の参照が可能

  各イテレータに対して様々な演算子を使用することができます.たとえば,it をある要素を指すイテレータとしたとき,「 it++ 」という演算によって,it は次の要素を指すことになります.ただし,すべてのイテレータにおいて同じ演算子を使用できるわけではありません.たとえば,「 -- 」という演算は,双方向またはランダムアクセスイテレータだけでしか使用できません.また,「 it += 2 」のような演算は,ランダムアクセスイテレータだけで使用できます.各イテレータにおいて使用できる演算子は以下に示すとおりです.

ランダムアクセスイテレータ *, ->, =, +, -, ++, --, [], <, >, <=, >=, -=, +=, ==, !=
双方向イテレータ *, ->, =, ++, --, ==, !=
前方イテレータ *, ->, =, ++, ==, !=
入力イテレータ *, ->, =, ++, ==, !=
出力イテレータ *, =, ++

  STL の各コンテナには,コンテナ固有のイテレータが定義されています.また,逆方向イテレータも定義されています.以下に示すのは,vector クラスに対してイテレータを使用した例です.

(プログラム例 14.3 ) イテレータ

#include <stdio.h>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	vector<int>::iterator it;   // イテレータ
	vector<int>::reverse_iterator r_it;   // 逆方向イテレータ
					// 初期設定
	printf("**初期設定**\n");
	v.push_back(0);
	v.push_back(4);
	v.push_back(3);
	v.push_back(1);
	v.push_back(2);
	for (it = v.begin(); it != v.end(); it++)
		printf("  %d", *it);
	printf("\n**後ろの 3 つを出力**\n");
	it = v.begin() + 2;   // vector の場合,ランダムアクセスイテレータであるため可能
	while (it != v.end()) {
		printf("  %d", *it);
		it++;
	}
	printf("\n**逆方向に出力**\n");
	for (r_it = v.rbegin(); r_it != v.rend(); r_it++)
		printf("  %d", *r_it);

	return 0;
}
		
(出力)
**初期設定**
  0  4  3  1  2
**後ろの 3 つを出力**
  3  1  2
**逆方向に出力**
  2  1  3  4  0		

  C++ には,イテレータを目的に合わせて変換するアダプタも用意されています.その一つが,要素を挿入するときに使用する挿入イテレータです.

insert_iterator コンテナに要素を挿入する出力イテレータ.insert_iterator を生成する inserter() 関数を持っています.
front_insert_iterator コンテナの最初に要素を挿入する出力イテレータ.push_front() を使用して実行するため,このメンバー関数を持つコンテナだけに適用できます.front_insert_iterator を生成する front_inserter() 関数を持っています.
back_insert_iterator コンテナの最後に要素を挿入する出力イテレータ.push_back() を使用して実行するため,このメンバー関数を持つコンテナだけに適用できます.back_insert_iterator を生成する back_inserter() 関数を持っています.

(プログラム例 14.4 ) 挿入イテレータ

#include <stdio.h>
#include <deque>
using namespace std;

int main()
{
					// 初期設定
	printf("**初期設定 : q1**\n");
	deque<int>::iterator it;
	deque<int> q1;
	for (int i1 = 0; i1 < 5; i1++)
		q1.push_back(i1);
	for (it = q1.begin(); it != q1.end(); it++)
		printf("  %d", *it);
	printf("\n");
	printf("**初期設定 : q2**\n");
	deque<int> q2;
	for (int i1 = 0; i1 < 3; i1++)
		q2.push_back(10 * (i1 + 1));
	for (it = q2.begin(); it != q2.end(); it++)
		printf("  %d", *it);
	printf("\n");
					// insert_iterator
	printf("**2 番目の要素の前に 5 を挿入( insert_iterator )**\n");
	it = q1.begin() + 1;
	insert_iterator<deque<int> > i_it(q1, it);
	*i_it = 5;
	for (it = q1.begin(); it != q1.end(); it++)
		printf("  %d", *it);
	printf("\n");
					// inserter
	printf("**q1 の 3 番目の要素の前に q2 を挿入( inserter )**\n");
	copy(q2.begin(), q2.end(), inserter(q1, q1.begin()+2));
	for (it = q1.begin(); it != q1.end(); it++)
		printf("  %d", *it);
	printf("\n");
					// front_insert_iterator, back_insert_iterator
	printf("**最初と最後に 20 と 10 を挿入**\n");
	printf("**( front_insert_iterator と back_insert_iterator )**\n");
	front_insert_iterator<deque<int> > f_it(q1);
	*f_it = 20;
	back_insert_iterator<deque<int> > b_it(q1);
	*b_it = 10;
	for (it = q1.begin(); it != q1.end(); it++)
		printf("  %d", *it);
	printf("\n");
					// back_inserter
	deque<int> q3;
	printf("**q2 を q3 にコピー( back_inserter )**\n");
	copy(q2.begin(), q2.end(), back_inserter(q3));
//	copy(q2.begin(), q2.end(), q3.begin());   // 挿入タイプでないとだめ
	for (it = q3.begin(); it != q3.end(); it++)
		printf("  %d", *it);
	printf("\n");
					// copy
	printf("**q2 を q1 に上書きコピー**\n");
	copy(q2.begin(), q2.end(), q1.begin());   // 上書きコピー
	for (it = q1.begin(); it != q1.end(); it++)
		printf("  %d", *it);
	printf("\n");

	return 0;
}
		
(出力)
**初期設定 : q1**
  0  1  2  3  4
**初期設定 : q2**
  10  20  30
**2 番目の要素の前に 5 を挿入( insert_iterator )**
  0  5  1  2  3  4
**q1 の 3 番目の要素の前に q2 を挿入( inserter )**
  0  5  10  20  30  1  2  3  4
**最初と最後に 20 と 10 を挿入**
**( front_insert_iterator と back_insert_iterator )**
  20  0  5  10  20  30  1  2  3  4  10
**q2 を q3 にコピー( back_inserter )**
  10  20  30
**q2 を q1 に上書きコピー**
  10  20  30  10  20  30  1  2  3  4  10
		

  入出力ストリームをイテレータで扱うこともできます.それを行うのがストリームイテレータであり,入力ストリームを対象とした istream_iterator,出力ストリームを対象とした ostream_iterator などがあります.

(プログラム例 14.5 ) ストリームイテレータ

#include <iostream>
#include <iterator>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	vector<int>::iterator it;
	istream_iterator<int> i_it(cin);
	ostream_iterator<int> o_it(cout);

	*i_it;
	while (*i_it != 0) {
		v.push_back(*i_it);
		i_it++;
	}
	for (it = v.begin(); it != v.end(); it++) {
		cout << "  ";
		*o_it = *it;
		o_it++;
	}

	return 0;
}
		
(出力) 10 20 30 0 を入力したとき
  10  20  30		

14.3.3 アルゴリズム等

  アルゴリズムは,関数テンプレートの一種です.各コンテナには様々なメンバー関数が存在しますが,アルゴリズムとの違いは,アルゴリズムでは異なるコンテナを扱えるといった点です.その主ものは以下に示すとおりです.なお,以下には,<algorithm> ヘッダを必要とするアルゴリズムと共に,C++ の標準ライブラリのいくつかも記述しています.そのため,1 列目には,2 列目以降に示したクラスや関数を使用するために必要なヘッダファイルも記述しています.

  今まで何度も述べてきたように,最も重要なことは,「読みやすく,理解しやすい」プログラムを書くことです.以下に示す表には,多くの関数やクラスが挙げてありますが,この点を十分考慮して使用してください.最近の C++ における改訂では,「記述しやすさ」だけのために,理解が困難な関数を導入し,C++ 離れを加速しているように思えますが,私の思い違いでしょうか.そこで,以下の表に示されているクラスや関数を使用する前に,下に示すプログラム例を読んでみてください.
01	#include <iostream>
02	#include <algorithm>
03	#include <numeric>
04	#include <vector>
05	
06	using namespace std;
07	
08	void print(int x) {
09		cout << "  " << x;
10	}
11	
12	int sq(int n) {
13		return n * n;
14	}
15	
16	int main()
17	{
18				// 最大値(max)
19		int a = 10, b = 5;
20					// max
21		int mx = max(a, b);
22	//	int mx = max({1, 10, 5, 3, 5});
23					// 条件演算子
24	//	int mx = a > b ? a : b;
25					// if 文
26	//	int mx = a;
27	//	if (b > mx)
28	//		mx = b;
29	
30		cout << "max :  " << mx << endl;
31				// 繰り返し
32		vector<int> v = {0, 1, 2, 3, 4};
33		cout << "v :";
34					// for_each,関数
35		for_each(v.begin(), v.end(), print);
36					// for_each,ラムダ式
37	//	for_each(v.begin(), v.end(), [](int x){ cout << "  " << x; });
38					// for 文
39	//	for (auto x : v)
40	//		cout << "  " << x;
41	
42		cout << endl;
43				// 部分和
44		vector<int> v1 = {1, 2, 3, 4, 5}, v2(5);
45		cout << "v1 :";
46		for (auto x : v1)
47			cout << "  " << x;
48		cout << endl;
49					// transform_inclusive_scan,二項関数オブジェクト plus と 関数 sq
50		transform_inclusive_scan(v1.begin(), v1.end(), v2.begin(), plus(), sq);   // 二項関数オブジェクト plus と 関数 sq
51					// transform_inclusive_scan,ラムダ式
52	//	transform_inclusive_scan(v1.begin(), v1.end(), v2.begin(), [](int sum, int a){ return sum + a; }, [](int x){ return x * x; });   // ラムダ式
53					// for 文
54	//	v2[0] = v1[0] * v1[0];
55	//	for (int i1 = 1; i1 < 5; i1++)
56	//		v2[i1] = v2[i1-1] + v1[i1] * v1[i1];
57	
58		cout << "v2 :";
59		for (auto x : v2)
60			cout << "  " << x;
61		cout << endl;
62	
63		return 0;
64	}
		
19 ~ 30 行目

  2 つの値の内,大きい方の値を求めています.21 行目では,<algorithm> ヘッダ内の関数 max を使用しています.max は,22 行目のように,初期化子リストを使用して複数個のデータを求めることができますが,基本的には 2 つのデータの最大値を求める関数です.2 つのデータの最大値は,max を使用しなくても,24 行目,または,26 ~ 28 行目のようにして簡単に求めることができます.いずれの方法においても,
   max :  10			
のような結果が得られます.max という名前から,その関数の意味するところは明らかですし,また,
   if (max(a,b) > x)			
のように文の中に埋め込んで記述することもできます.従って,必要な箇所では使用しても構わないと思います.

  32 ~ 42 行目は,vector クラスのオブジェクト v の各要素を出力しています.35 行目では,<algorithm> ヘッダ内の関数 for_each を使用しています.for_each は,指定した範囲に,指定した関数による処理を適用する関数です.この例では,与えられたデータを出力する print という関数( 08 ~ 10 行目)を使用しています.37 行目は,for_each に対してラムダ式を使用した場合,39 ~ 40 行目は,普通の for 文を使用した場合を表しています.いずれの場合においても,
   v :  0  1  2  3  4			
のような結果が得られます.確かに,for_each を使用すると記述は簡単になりますが,for 文を使用してもそれほど多くの記述は必要とせず,理解もしやすいことを考えると,あえて使用する意味が分かりません.print 関数を使用した場合は,その本体が記述されている部分に視点を移動しなければなりませんし,ラムダ式は,慣れない人にとっては理解しにくいと思います.私が古い人間であるかもしれませんが,いずれにしても,for_each よりも for 文の方が,理解しやすく感じます.

  44 ~ 61 行目は,vector クラスのオブジェクト v1 に対して,v2[n] に v1 の n 番目( n : 0 ~ 4 )までの各要素の 2 乗の和を保存しています.49 行目では,<numeric> ヘッダ内の関数 transform_inclusive_scan を使用しています.transform_inclusive_scan は,イテレータで指定された範囲の部分和(順番に和を求めながら,その段階までの和をその要素の値とする)を計算する関数です.関数 sq( 12 ~14 行目)では,各要素の 2 乗を計算し,二項関数オブジェクト plus では,その和を計算しています.52 行目は,transform_inclusive_scan に対してラムダ式を使用した場合,54 ~ 56 行目は,普通の for 文を使用した場合を表しています.いずれの場合においても,
   v1 :  1  2  3  4  5
   v2 :  1  5  14  30  55			
のような結果が得られます.確かに,transform_inclusive_scan を使用すると記述は簡単になりますが,よほど慣れた人でないと,その実行内容を想像もできません.for 文を使用してもそれほど多くの記述は必要とせず,理解もしやすいことを考えると,あえて使用する意味が全く分かりません.

シーケンス変更なし <algorithm> adjacent_find 指定した範囲で,同じ要素が連続している(二項関数オブジェクトで指定された関係にある)位置を探します.
all_of 範囲内の全ての要素が条件を満たすかを判定します.
any_of 範囲内のいずれかの要素が条件を満たすかを判定する.
count 指定した範囲で,指定した値と一致する要素の数を返します.
count_if 指定した範囲で,単項関数オブジェクトが真となる要素の数を返します.
equal first1 から last1 で指定した範囲が,first2 で指定された要素列と同じ(二項関数オブジェクトで指定した意味で同じ)場合に true を返します.
find 指定した範囲で,指定した要素を探します.
find_end first1 から last1 で指定した範囲内に,first2 から last2 に現れるシーケンスと同じ(二項関数オブジェクトで指定した意味で同じ)シーケンスが最後に現れた位置を返します.
find_first_of first1 から last1 で指定した範囲で,first2 から last2 に含まれるいずれかの要素と等しい(二項関数オブジェクトで指定した意味で等しい)要素が最初に表れた位置を返します.
find_if 指定した範囲で,単項関数オブジェクトが真になる要素を探します.
find_if_not 範囲の中から指定された条件を満たさない最初の要素を検索します.
for_each 指定した範囲に,指定した関数による処理を適用します.
for_each_n 範囲の先頭 n 個の要素に指定された関数を適用します.
mismatch first1 から last1 で指定した範囲が,first2 で指定された要素列と異なる(二項関数オブジェクトで指定した意味で異なる)位置を返します.
none_of 範囲内の全ての要素が条件を満たさないかを判定する.
search first1 から last1 で指定した範囲で,first2 から last2 で指定された要素列(二項関数オブジェクトで指定した意味で等しい要素列)が最初に表れた位置を返します.
search_n first1 から last1 で指定した範囲で,指定された要素の n 個の要素列(二項関数オブジェクトで指定した意味で指定した要素と等しい)最初に表れた位置を返します.
シーケンス変更あり <algorithm> copy 指定した範囲をコピーします.
copy_backward 指定した範囲を後方からコピーします.
copy_if 指定した範囲内の条件を満たした要素をコピーします.
copy_n 指定した位置から,指定した数の要素をコピーします.
fill 指定した範囲の要素を,指定した要素に設定します.
fill_n 指定した位置から始まる指定した数の要素を,指定した要素に設定します.
generate 指定した範囲の要素を,指定した関数によって生成された要素に設定します.
generate_n 指定した位置から始まる指定した数の要素を,指定した関数によって生成された要素に設定します.
is_partitioned 与えられた範囲が条件によって区分化されているか判定します.
iter_swap イテレータで指定した 2 つの要素を交換します.
partition 単項述語オブジェクトが真になる要素を偽になる要素の前に移動します.その際,最初の前後関係が維持されるとは限りません.
partition_copy 与えられた範囲を条件によって 2 つの出力の範囲へ分けてコピーします.
partition_point 与えられた範囲から条件によって区分化されている位置を得ます.
remove 指定した範囲の指定した要素を削除します.ただし,コンテナのサイズ自身は変化しません.
remove_copy 指定した範囲の指定した要素を削除し,指定した場所にコピーします.ただし,コピー元の要素は変化しません.
remove_copy_if 指定した範囲の単項関数オブジェクトが真になる要素を削除し,指定した場所にコピーします.ただし,コピー元の要素は変化しません.
remove_if 指定した範囲の単項関数オブジェクトが真になる要素を削除します.ただし,コンテナのサイズ自身は変化しません.
replace 指定した範囲の指定した要素を,指定した要素に置き換えます.
replace_copy 指定した範囲の指定した要素を,指定した要素に置き換え,指定した場所にコピーします.ただし,コピー元の要素は変化しません.
replace_copy_if 指定した範囲の単項関数オブジェクトが真になる要素を,指定した要素に置き換え,指定した場所にコピーします.ただし,コピー元の要素は変化しません.
replace_if 指定した範囲の単項関数オブジェクトが真になる要素を,指定した要素に置き換えます.
reverse 指定した範囲の要素の並び順を反転します.
reverse_copy 指定した範囲の要素の並び順を反転し,指定した場所にコピーします.ただし,コピー元は変化しません.
rotate イテレータ middle が指す要素が先頭になるように左方向に回転します.
rotate_copy イテレータ middle が指す要素が先頭になるように左方向に回転し,指定した場所にコピーします.ただし,コピー元は変化しません.
sample 指定された範囲から指定された個数の要素をランダムに抽出します.
shuffle 指定した範囲の要素を同じ確率で並び替える.
stable_partition 単項述語オブジェクトが真になる要素を偽になる要素の前に移動します.その際,最初の前後関係が維持されます.
swap_ranges イテレータ first1,last1 で指定した範囲を first2 で始まる範囲と交換します.
transform イテレータで指定された範囲の要素に対して,1 引数関数,または,2 引数関数の結果を適用し,指定した場所に保存します.
unique 指定した範囲内の隣り合った重複した要素(二項関数オブジェクトを真にする意味で隣り合った重複した要素)を削除します.ただし,コンテナのサイズ自身は変化しません.
unique_copy 指定した範囲内の隣り合った重複した要素(二項関数オブジェクトを真にする意味で隣り合った重複した要素)を削除し,指定した位置にコピーします.ただし,コピー元は変化しません.
ソート関係 <algorithm> binary_search ソート済みの系列に対して,指定した範囲で,指定した要素を探します.
clamp 値 v を指定した範囲 [low, high] 内に収めます.
equal_range ソート済みの系列に対して,指定した要素を系列の順序を破壊することなく挿入できる位置( lower_bound と upper_bound,value 以上の値の位置と value より大きい値の位置)を返します.
includes 最初の系列に後ろの系列のすべての要素が含まれているとき true を返します.
inplace_merge 2つの連続したソート済み範囲(同じ系列内の first から middle で指定した部分と,middle から last で指定した部分)をマージします.
is_heap 範囲がヒープ化されているかを判定します.
is_heap_until 範囲がヒープ化されているか判定し,ヒープ化されていない最初の要素を指すイテレータを返します.
is_permutation first2 または [first2, last2] で示された並びが,[first1, last1] を並び替えたものに存在するか否かを返す.
is_sorted 与えられた範囲がソート済みかを判定します.
is_sorted_until ソート済みか判定し,ソートされていない位置のイテレータを返します.
lexicographical_compare 2 つの系列をアルファベット順に比較し,先の系列が次の系列より辞書的に前にあれば true を返します.
lower_bound ソート済みの系列に対して,指定した要素以上の値を持つ最初の要素を返します.
make_heap ヒープを構築します.
max 2 つの値の内,または,初期化子リストの中から大きい方の値を返します.
max_element 指定した範囲において,最大要素を指す最初のイテレータを返します.
merge 2つのソート済み範囲をマージします.
min 2 つの値の内,または,初期化子リストの中から小さい方の値を返します.
min_element 指定した範囲において,最小要素を指す最初のイテレータを返します.
minmax 2 つの値の内,または,初期化子リストの中から最小値と最大値の組を返します.
minmax_element 指定した範囲において,最小要素及び最大要素を指すイテレータの組を返します.
next_permutation 系列の各要素を辞書的順序で並べた場合において,現在の並べ方の次になる並べ方を返します.次の並べ方が存在しない場合,辞書的順序における最初の並べ方に並べ替えます.
nth_element first から last によって指定した範囲を対象とし,nth で指定した要素より小さい要素をその前に,大きい要素をその後ろに移動します.
partial_sort first から last で指定した範囲をソートします.その際,ソートされるのは first から middle までであり,それ以降はばらばらとなります.
partial_sort_copy first から last で指定した範囲をソートし,他の範囲( result_first から result_last の間)に可能な限りコピーします.
pop_heap ヒープの先頭と末尾を入れ替え,ヒープを再構築します.末尾の要素は,pop_heap を実行しても,コンテナ内から要素が削除されるわけではなく,系列の最後に移動し,ヒープから外れるだけです.
prev_permutation 系列の各要素を辞書的順序で並べた場合において,現在の並べ方の前になる並べ方を返します.前の並べ方が存在しない場合,辞書的順序における最後の並べ方並べ替えます.
push_heap ヒープに要素を追加し,ヒープを再構築します.追加する要素は,前も使用するコンテナに追加しておく必要があります.
set_difference 系列 1 に含まれ,系列 2 に含まれない要素の集合( 2 つのソート済み範囲の差集合)を出力します.なお,2 つの系列は前もってソートしておく必要があります.
set_intersection 系列 1 ,及び,系列 2 の両方に含まれる要素の集合( 2 つのソート済み範囲の積集合)を出力します.なお,2 つの系列は前もってソートしておく必要があります.
set_symmetric_difference 系列 1 ,および,系列 2 に共通しない要素( 2 つのソート済み範囲の対称差集合)の集合を出力します.なお,2 つの系列は前もってソートしておく必要があります.
set_union 系列 1 ,または,系列 2 に含まれる要素の集合( 2 つのソート済み範囲の和集合)を出力します.なお,2 つの系列は前もってソートしておく必要があります.
sort 指定した範囲を並べ替えます.
sort_heap ヒープをソートします.
stable_sort 指定した範囲をソートします.その際,等しい要素の並び順が保持されます.
upper_bound ソート済みの系列に対して,指定した要素より大きい値を持つ最初の要素を返します.
bitset クラス <bitset> bitset N ビットのビット集合を表すクラスです.添字演算子で任意の位置のビット状態を確認でき,文字列と整数値との相互変換が可能です.
数学関係 <cmath> 三角関数,逆三角関数 三角関数,逆三角関数
双曲線関数,逆双曲線関数 双曲線関数,逆双曲線関数
指数関数,対数関数 指数関数,対数関数
累乗,冪乗,絶対値 冪乗,平方和,平方根,立方根,絶対値
切り上げ,切り捨て,四捨五入 天井関数,床関数,四捨五入,0 方向への丸め
剰余 商と剰余
誤差関数,ガンマ関数 誤差関数,相補誤差関数,ガンマ関数,ガンマ関数の絶対値の自然対数
特殊関数 ラゲール陪多項式,ルジャンドル陪関数,ベータ関数,楕円積分,ベッセル関数,指数積分,エルミート多項式,ラゲール多項式,ルジャンドル多項式,リーマンのゼータ関数,球面調和関数のθ成分
複素数 <complex> 複素数 複素数を扱うクラスです.
数値シーケンス <numeric> accumulate イテレータで指定された範囲の総和を計算します.
adjacent_difference 隣接する要素間の差からなる系列を計算します.
exclusive_scan イテレータで指定された範囲の部分和(順番に和を求めながら,その段階までの和をその要素の値とする)を計算します.なお,inclusive_scan とは異なり,最後の要素は含まれず,また,生成される要素列の最初の要素は初期値に一致します.
gcd 最大公約数を求めます.
inclusive_scan イテレータで指定された範囲の部分和(順番に和を求めながら,その段階までの和をその要素の値とする)を計算します.なお,生成される要素列の最初の要素は,必ず,元の系列の最初の要素に一致します.
inner_product イテレータで指定された範囲で,2 つのコンテナ v1 と v2 の内積を計算します.
iota 指定された値から始まる整数列を生成します.
lcm 最小公倍数を求めます.
partial_sum イテレータで指定された範囲の部分和(順番に和を求めながら,その段階までの和をその要素の値とする)を計算します.
reduce イテレータで指定された範囲の総和を計算します.
transform_exclusive_scan イテレータで指定された範囲の部分和(順番に和を求めながら,その段階までの和をその要素の値とする)を計算します.部分和を求める際,最後の要素は除外されます.
transform_inclusive_scan イテレータで指定された範囲の部分和(順番に和を求めながら,その段階までの和をその要素の値とする)を計算します.部分和を求める際,最後の要素も対象とします.
transform_reduce イテレータで指定された範囲の総和を計算します.
乱数 <random> Bernoulli ベルヌーイ分布乱数,二項分布乱数,幾何分布乱数,負の二項分布乱数を生成します.
mt19937 メルセンヌ・ツイスター法による擬似乱数生成エンジンです.
normal 正規分布乱数,対数正規分布乱数,カイ二乗分布乱数,コーシー分布乱数,フィッシャーの F 分布乱数,ステューデントの t 分布乱数を生成します.
Poisson ポワソン分布乱数,指数分布乱数,ガンマ分布乱数,ワイブル分布乱数,極値分布乱数を生成します.
sampling 確率分布生成器,重み付き確率分布生成器,線形重み付き確率分布生成器を生成します.
uniform 一様分布乱数を生成します.
文字列 <string> string 文字列を保持するクラスです.
タプル <tuple> tuple 複数の型の値を保持する「タプル」を表現するためのクラスです.
ユーティリティ <utility> pair 2 つのデータをペアにして扱うためのクラスです.
swap 2 つの値を入れ替えます.

14.3.4 関数オブジェクト

  関数オブジェクトは,関数呼び出し演算子( "()" )をオーバーロードしたものです.オブジェクトですが,関数のように使用することが可能です.STL の多くの場所で使用されています.関数オブジェクトには,1 つの引数をとる単項関数オブジェクトと,2 つの引数をとる二項関数オブジェクトがあります.C++ に組み込まれている関数オブジェクトには以下に示すようなものがあります.

単項関数オブジェクト logical_not, negate
二項関数オブジェクト plus, minus, multiplies, divides, modulus, equal_to, not_equal_to, greater, greater_equal, less, less_equal, logical_and, logical_or

  以下に示すのは,map で greater と less を使用した例です.なお,関数オブジェクトを使用するためには,<functional> ヘッダが必要です.

(プログラム例 14.6 ) map における関数オブジェクトの使用

#include <iostream>
#include <map>
using namespace std;

int main()
{
	cout << "**コンテナ m1 (greater)**\n";
	map<string, int, greater<string> > m1;
	map<string, int, greater<string> >::iterator g_it;
	m1.insert(pair<string, int>("suzuki", 40));
	m1.insert(pair<string, int>("sato", 60));
	m1.insert(pair<string, int>("yamada", 70));
	m1.insert(pair<string, int>("tanaka", 50));
	m1.insert(pair<string, int>("kato", 90));
	for (g_it = m1.begin(); g_it != m1.end(); g_it++)
		cout << "  " << (*g_it).first << " " << (*g_it).second;
	cout << endl;

	cout << "**コンテナ m2 (less)**\n";
	map<string, int, less<string> > m2;
	map<string, int, less<string> >::iterator l_it;
	m2.insert(pair<string, int>("suzuki", 40));
	m2.insert(pair<string, int>("sato", 60));
	m2.insert(pair<string, int>("yamada", 70));
	m2.insert(pair<string, int>("tanaka", 50));
	m2.insert(pair<string, int>("kato", 90));
	for (l_it = m2.begin(); l_it != m2.end(); l_it++)
		cout << "  " << (*l_it).first << " " << (*l_it).second;
	cout << endl;

	return 0;
}
		
(出力)
**コンテナ m1 (greater)**
  yamada 70  tanaka 50  suzuki 40  sato 60  kato 90
**コンテナ m2 (less)**
  kato 90  sato 60  suzuki 40  tanaka 50  yamada 70		

  関数オブジェクトは自分で作成することもできます.STL の中で使用するには,下に示す例で示すように,unary_function や binary_function を継承した方が作りやすいと思います.単項関数オブジェクトは,クラステンプレートで記述してありますが,クラステンプレートを使用せず,かつ,unary_function を継承しない方法についても,コメントとして示してあります.また,二項関数オブジェクトに対しても,binary_function を継承しない方法をコメントとして示してあります.なお,以下の例では,二項関数オブジェクトを STL 以外の場所で使用しています( 66 行目).同じように,関数オブジェクトを一般の関数の引数に使用することも可能です(「プログラム例 7.14 」参照).

(プログラム例 14.7 ) 関数オブジェクトの作成

01	#include <iostream>
02	#include <list>
03	#include <functional>
04	using namespace std;
05						// 単項関数オブジェクト
06	template <class T> class is_odd : public unary_function<T, bool>   // bool は戻り値
07	{
08		public:
09			bool operator() (T k)
10			{
11				return (bool)(k % 2);   // 0 以外は true に変換される
12			}
13	};
14	/* クラステンプレートを使用せず,かつ,unary_function を継承しない場合
15	class is_odd
16	{
17		public:
18			bool operator() (int k)
19			{
20				return (bool)(k % 2);   // 0 以外は true に変換される
21			}
22	};
23	*/
24						// 二項関数オブジェクト
25	class Equal : public binary_function<int, int, bool>   // bool は戻り値
26	{
27		public:
28			result_type operator() (first_argument_type a, second_argument_type b)
29			{
30				return (result_type)(a == b);
31			}
32	};
33	/* binary_function を継承しない場合
34	class Equal
35	{
36		public:
37			bool operator() (int a, int b)
38			{
39				return (bool)(a == b);
40			}
41	};
42	*/
43	
44	int main()
45	{
46		list<int> ls;
47		list<int>::iterator it;
48						// 初期設定
49		printf("**初期設定**\n");
50		ls.push_back(0);
51		ls.push_back(2);
52		ls.push_back(3);
53		ls.push_back(4);
54		ls.push_back(1);
55		for (it = ls.begin(); it != ls.end(); it++)
56			cout << "  " << *it;
57		cout << endl;
58						// 奇数である要素を削除
59		printf("**奇数である要素を削除**\n");
60		ls.remove_if(is_odd<int>());   // 単項関数オブジェクト
61	//	ls.remove_if(is_odd());   // クラステンプレートを使用しない場合
62						// 結果を表示
63		Equal eq;   // 二項関数オブジェクト
64	//	equal_to<int> eq;   // 組み込まれている equal_to を使用する場合
65		for (it = ls.begin(); it != ls.end(); it++) {
66			if (eq(*it, 2))
67				cout << "  " << *it << " は 2 に等しい" << endl;
68			else
69				cout << "  " << *it << " は 2 に等しくない" << endl;
70		}
71	
72		return 0;
73	}
		
(出力)
**初期設定**
  0  2  3  4  1
**奇数である要素を削除**
  0 は 2 に等しくない
  2 は 2 に等しい
  4 は 2 に等しくない		

14.4 ラムダ式C++11

  ラムダ式lambda expressions )は,簡単に関数オブジェクトを定義するための機能です.関数の引数として記述することも可能です(「プログラム例 7.14 」参照).十分理解していれば,プログラムの記述を簡単に行うことができますが,慣れない人にとっては理解しにくい場合も多いと思います.そのため,「読みやすく,理解しやすい」プログラムを書くという基本に外れる可能性も高く,特別な場合を除き,使用は避けた方が良いと思います.ラムダ式の一般的形式は以下に示すとおりです.
[キャプチャリスト](パラメータリスト){ 関数本体 }		
  パラメータリストは,関数の引数に相当し,パラメータリストがない場合は括弧( () )を省略して構いません.キャプチャリスト(キャプチャとは,ラムダ式からその外側の変数を使用する機能のこと)は,関数本体の中で利用したいプログラム内の自由変数のリストです.ただし,ラムダ式の定義の前に出現した変数でなければなりません.キャプチャリストの記述方法は以下に示すとおりです.

(プログラム例14.8) ラムダ式

01	#include <iostream>
02	#include <vector>
03	#include <algorithm>
04	
05	using namespace std;
06	
07	int fun(auto sb, int a, int b) {
08		return sb(a, b);
09	}
10	
11	int main()
12	{
13				// ラムダ式の定義と利用
14		int a = 10, b = 5;
15	
16		cout << "ラムダ式定義前 : a " << a << ",b " << b << ",c 未定義\n";
17		auto fun1 = [a, &b](int x, int y){ b *= 10; return a * x + b * y; };
18	//	auto fun1 = [=, &b](int x, int y){ b *= 10; return a * x + b * y; };  // ok
19					// a は変更不可のため,下の定義は error, 
20	//	auto fun1 = [=, &b](int x, int y){ a *= 10; return a * x + b * y; };
21	
22		auto fun2 = [a, b](int x){ return a * x + b; };
23	//	auto fun2 = [=](int x){ return a * x + b; };   // ok
24					// この時点で変数 c は未定義のため,下の定義は error, 
25	//	auto fun2 = [=](int x){ return a * x + b + c; };
26	
27		int c = 5;
28		a     = 20;
29					// ラムダ式を定義した時点での変数 a の値がコピーされるため
30					// ラムダ式を使用する際の a の値は 10 となる
31		cout << "ラムダ式使用前 : a " << a << ",b " << b << ",c " << c << endl;
32		int res1  = fun1(1, 2);
33		int res2  = fun2(3);
34		auto fun3 = fun1;   // 他の変数に代入 OK
35		int res3  = fun3(1, 2);
36		cout << "ラムダ式使用後 : a " << a << ",b " << b << ",c " << c << endl;
37		cout << "   fun1 : " << res1 << ",fun2 : " << res2  << ",fun3 : " << res3 << endl;
38				// 関数の引数として利用(その1)
39		vector<int> v {1, 2, 3, 4, 5};
40		cout << "vector v :";
41		for_each(v.begin(), v.end(), [a](int x){ cout << "  " << a * x; });
42		cout << endl;
43				// 関数の引数として利用(その2)
44		auto add = [](int x, int y) { return x + y; };
45		auto sub = [](int x, int y) { return x - y; };
46		auto add_sub = [](auto sb, int x, int y) { return sb(x, y); };
47		cout << "add(add_sub) : " << add_sub(add, 2, 3) << ",sub(add_sub) : " << add_sub(sub, 2, 3)  << endl;
48		cout << "add(fun) : " << fun(add, 2, 3) << ",sub(fun) : " << fun(sub, 2, 3)  << endl;
49	
50		return 0;
51	}
		
17 行目

  ラムダ式 fun1 を定義しています.その際,14 行目で定義された変数 a 及び b を使用しており,b の値はラムダ式の中で変更されます( 10 倍される).

22 行目

  ラムダ式 fun2 を定義しています.

32 ~ 33 行目

  ラムダ式 fun1,及び,fun2 を実行しています.28 行目で a の値が変更されていますが,ラムダ式の実行の際は,これらのラムダ式が定義された時点における a の値 10 が使用されます.ただし,fun1 において参照型として使用されている変数 b に対しては,fun1 が実行されるたびに変化していきます( fun2 には影響を与えない).

34 ~ 35 行目

  ラムダ式 fun1 を変数 fun3 に代入した後,実行しています.ラムダ式においては,このような代入も可能です.

39 ~ 42 行目

  ラムダ式を,C++ 標準ライブラリ内の for_each に引数として渡しています.

44 ~ 48 行目

  46 ~ 47 行目では,ラムダ式 add 及び sub を,他のラムダ式 add_sub に引数として渡しています.48 行目では,ラムダ式 add 及び sub を,通常の関数 fun に引数として渡しています.
(出力)
ラムダ式定義前 : a 10,b 5,c 未定義
ラムダ式使用前 : a 20,b 5,c 5
ラムダ式使用後 : a 20,b 500,c 5
   fun1 : 110,fun2 : 35,fun3 : 1010
vector v :  20  40  60  80  100
add(add_sub) : 5,sub(add_sub) : -1
add(fun) : 5,sub(fun) : -1		

演習問題14

[問1]整数( int ),実数( double ),及び,文字列(クラスとして定義)の加算を行う関数テンプレート plus と,それらの結果の出力を行う関数 print を定義せよ.ただし,文字列の加算とは,文字列の連結を意味するものとする.

[問2] n 個の任意の型のデータをディスクから配列に読み込み,ソートして配列に保存するクラステンプレートを作成せよ.

[問3] n 個( 15 個以上)の異なる整数データを読み込み( vector に保存),昇順にソートした後,小さい方から 5 つ,及び,大きい方から 5 つ以外のデータを全て削除し,残りのデータを出力すると共にそれらの平均値を出力するプログラムを書け.

[問4] n 個( 10 個以上)の異なる整数データを set に読み込み,小さい方から 5 つのデータの平均値を出力するプログラムを書け.ただし,set に保存するデータ数は 5 以下とし,データの数が 5 を超えた場合は,入力されたデータを無視するか,または,一番最後のデータ(最も大きなデータ)を削除した後入力されたデータを set に追加するか,いずれかの方法を用いるものとする.

[問5] n( 10 以上)人のテストの点数(全て異なる整数,キーとする)と名前( string )を map に保存し,上位 5 人の名前と点数を出力するプログラムを書け.

[問6]上と同じ問題を,点数と名前をメンバー変数として持つクラスを使用して書け( set を利用).

情報学部 菅沼ホーム 全体目次 演習解答例 付録 索引