はじめに
マネージド言語の発展ペースに比べると、C++言語の変化は誠に遅々たるものに見えますが、C++での新機能の開発は着実なペースで以前と同様に続いており、全体的に見ればこの点こそがC++という言語の特徴と言えます。Visual Basicのような、トレンドに合わせてその姿形を変えていく言語と比べると、長い目で見ての安定性はC++の大きな特長の1つです。C++では、変更は安定したペースで、かつ十分に精査されたうえで起こります。現在進行中のC++言語への拡張は「C++0x Standard」と呼ばれており、この拡張では、ここ10年間に標準として承認された事項を反映しています。Technical Report 1(TR1)は、こうした標準化プロセスでの1つの中間段階であり、最終的には標準の一部になると見込まれる新機能を数多く盛り込んでいます。
このMFC機能パックとそのインストールについては
以前の記事である程度触れたので、そこで述べた説明やヒントは、ここでは繰り返さないでおきます。
STLの配列
これまでのSTLコンテナのコレクションに欠けていた最も重要な機能の1つは、固定長の配列です。Cスタイルの配列や、vector、サードパーティーのライブラリを使用するなど、代替となる手法は数多くありますが、一般のC++プログラマのニーズを考えれば、固定長配列をSTLに追加することは必然です。今回新たに追加された配列型コレクションの使い方は非常にシンプルです。配列の長さをテンプレートのパラメータとして指定すると、そのコレクションはSTLのvectorコレクションと大変よく似た動きをするようになります。
#include <array>
#include <iostream>
#include <string>
std::tr1::array<int, 3> intArray = {4, 8, 10};
std::tr1::array<int, 3> intArray2 = {4, 8};
intArray2[2] = 10;
std::cout << ((intArray == intArray2)?
std::string("Arrays are equal"):
std::string("Arrays aren't equal")
);
std::cout << std::endl;
for (std::tr1::array<int, 3>::const_iterator it =
intArray2.begin();
it != intArray2.end();
++it) {
std::cout << " " << *it;
}
このサンプルコードには注目すべき点がいくつかあります。配列の全内容または一部の内容はイニシャライザで指定することができ、コンパイラは、イニシャライザの型と長さをチェックして、イニシャライザが配列の定義に従っていることを確認できます。==演算子もオーバーライドされており、配列内の各要素の値同士を比較できます。また、イテレータは他のSTLコレクションとまったく同じように機能します。さらに注目すべき点は、C++ 2008機能パックの一部としてリリースされるTR1拡張はすべてstd::tr1名前空間に含まれているということです。
正規表現
C++で正規表現を使用するためのツールがサードパーティーから数多く出ていますが、標準C++ライブラリとの統合を高いレベルで実現しているものはほとんどありません。正規表現がC++に直接追加されたことにより、STLの一部を構成するアルゴリズムで正規表現の実行結果を使用したり、さまざまなコンパイラでコンパイルできるコードを記述できるようになりました。
正規表現ライブラリの実装は、C++標準の
basic_stringクラスに似ています。TR1の正規表現ライブラリのうち、最も重要なクラスは
basic_regexで、このクラスは正規表現をラップし、
charまたは
wcharのどちらの文字型を使うかを
basic_regexのテンプレートパラメータで指定します。
basic_stringと同様に、
typedefでは
char版および
wchar版の
basic_regexがあらかじめ定義されています。
basic_regexクラスは、正規表現文字列と、大文字小文字の区別などの振る舞いや、使用する正規表現言語(basic、extended、ECMAScript、awk、grep、egrepをすべてサポート)を指定するフラグに基づいて正規表現オブジェクトを生成し、このオブジェクトが、ターゲット文字列と共にいずれかの正規表現テンプレート関数に渡されます。
利用できるテンプレート関数は
regex_match、
regex_search、
regex_replaceの3つです。
regex_match関数では、ターゲット文字列が、渡された
basic_regexオブジェクトに一致しているかを判定します。
regex_search関数は、正規表現に一致するテキストがターゲット文字列内にあるかどうかを判定し、必要な場合には、
match_resultsオブジェクトに正規表現キャプチャグループを割り当てることができます。残る
regex_replace関数では、正規表現の実行結果に基づいて文字列を置き換えることができます。
下記のサンプルコードでは、電子メールアドレスの妥当性確認を行う、ごく単純な正規表現を2つの文字列定数に対して実行しています(ここでのポイントは正規表現の内容ではありません。実際に電子メールアドレスの妥当性確認を行うには、このサンプルでは単純すぎます)。この例では、正規表現がターゲット文字列と一致するかどうかを出力します。
#include <regex>
#include <iostream>
#include <string>
basic_regex<char> regex("[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,4}",
basic_regex<char>::icase);
cout<< std::boolalpha << regex_match("GoOD@DOMAIN.COM", regex)
<< std::endl;
cout<< std::boolalpha << regex_match("@DOMAIN.COM", regex)
<< std::endl;
関数オブジェクト
関数オブジェクトは、以前に
Delegateクラスを使ったことのあるC++/CLI開発者には馴染みのあるものでしょう。関数オブジェクトは型安全である関数ポインタを提供し、このポインタは、関数実行の一環でアクセスしなければならない追加データも含むことができます。型安全ではない生の関数ポインタとは異なり、TR1関数テンプレートでは、関数を戻り値の型とパラメータによって定義することができ、このシグネチャを満たす関数についてはテンプレートインスタンスの一部としてインスタンス化し、キャストせずに呼び出すことができます。以下のコードは関数オブジェクトを使用して関数を呼び出す方法を示しています。
bool PrintData(int i, double d){
cout << i;
cout << endl;
cout << scientific << d;
cout << endl;
return true;
}
{
function<bool (int, double)> myPrint(PrintData);
bool res = myPrint(1, 2.0);
}
関数オブジェクトの明らかなメリットは、キャストの必要がないことです。この例の
myPrintオブジェクトは、複雑で危険性もある関数ポインタへのキャストを使わずに
PrintData関数を呼び出すことができます。
その他の追加
TR1のアップデートには、包括的な乱数生成ライブラリも含まれています。TR1に含まれる乱数生成スキーマとしては、
bernoulli_distribution、
binomial_distribution、
exponential_distribution、
gamma_distribution、
geometric_distribution、
normal_distribution、
poisson_distributionなどがあります。分布クラスは、TR1ライブラリに含まれている
linear_congruentialや
mersenne_twisterなどの乱数生成アルゴリズムによって使用されます。次のコードの抜粋は、
mersenne_twisterアルゴリズムと
gamma_distributionを使った乱数生成の例を示しています。
using namespace std::tr1;
...
//use TR1 typedef mt19937 for Mersenne twister algorithm
mt19937 randomAlg;
gamma_distribution <double> randomDist;
double d = randomDist(randomAlg);
TR1には、新たに
unordered_set、
unordered_map、
unordered_multimapという配列コレクションが加わっています。型名からわかるとおり、この3つのコレクションは、保守するシーケンスを順序どおりに並べないという点で、既存のSTLセットやマップコレクションと異なっています。整列しないことのメリットは、要素の挿入、削除、配置のコストがまったくないか、1回の操作で済むということです(つまり、コレクションサイズの増大に応じて計算のコストが増えることはありません)。反対に、整列しないことによる主なデメリットは、コレクションの同一性の判断が難しくなるという点です。同じ要素を含むコレクションでも、対応する要素が異なる場所に配置されている場合があるからです。
まとめ
今回のTR1アップデートは、C++0xの登場に向けた1ステップであり、標準C++の著しい進化を示すものです。この進化によって、C++言語が当面のあいだは強力で実用的、かつ時代に即したものであり続けることは確かです。TR1はマネージド言語のプログラマが期待するさまざまなメリットと機能をC++にもたらし、ソフトウェア業界がこれまで行ってきたC++コードへの多額の投資をこれからも支えていくことになるでしょう。
Visual C++ 2008機能パックにおけるTR1アップデートのリリースは、Microsoftが引き続きVisual C++製品への投資とサポートを行うことの意思表示であり、同時に、まだ何年かはネイティブコードの開発者が仕事にありつけるということでもあります。