|
vol.01 vol.02 vol.03 vol.04 vol.05 vol.06 vol.07 vol.08 vol.09
vol.10 vol.11 vol.12 vol.13 ML
テンプレートとSTL |
|
漁師プログラマ 山崎敏 YAMAZAKI Satoshi
vol.06 |
|
|
●●ある日 |
|
|
「一身上の都合で退職させていただきます」と退職届を書きながら「一身上の都合」とはどういう意味なのだろうか?と筆者は考えていました.14年間勤めた会社を辞めるのには「こういう理由で辞めます」と一言ではなかなか言い表せないものだし,なんとも都合の良い言葉があるものだとも思っていました.筆者の場合は,ありがちな上司との衝突ではなくて,純粋に会社全体の方針とプログラマとして働きつづけたいという筆者の想いとの違いがおもな要因でした.
この連載が終わったら次の仕事を探さないと….
どなたか,おもしろい仕事がありましたら紹介していただけませんでしょうか?(こらこら)14年前,筆者が入社したころ「プログラムが作れるならどんな仕事でも良い」と思っていました.ところが,14年間会社の中でプログラミング(システム開発)をしてきて,「どうせ作るなら自分の望みに近い仕事をしたい」と思うようになってしまったのです.理想と思えるのならと不自由を覚悟して入った世界なのに,不自由に長く浸かるとやはり自由な世界を求めてしまうようで,人間とは勝手な生き物のようですね.
|
|
|
●●タイプセーフの考え方 |
|
|
●Cコンパイラとの出会い
14年も昔の話なので正確な記憶ではないのですが,筆者は入社した年の最初のボーナスでX68000(通称「初代」)というパソコンを買いました.なんとこのX68000には,当時としてはまだ珍しかったCコンパイラが格安で提供されていたのです.
C言語の存在はそれ以前から知ってはいたものの,なかなか手の出せない代物でした.そのため,実際にC言語で本格的なプログラムを作るのはこのときがはじめてでした.それまでBASICやZ80アセンブラや6502アセンブラなどをおもに使っていた筆者にとって,C言語は「高級アセンブラ」という新たな世界が広がったように感じられ,とても興奮しました.BASICインタプリタに比べれば10倍以上高速に感じましたし,アセンブラに比べれば10倍以上可読性の良いコードが書けました.まさに夢の言語でした.
●タイプセーフとは
またBASICやアセンブラにはなかった「タイプセーフ」という考え方にメリットを感じたのもこのころです.整数(int)や実数(double)には「型」が決まっていて,基本的に違う型の代入や演算はコンパイルエラーになります.このチェック機能によって,引数の間違いや不正な代入や演算による単純なバグを実行する前に知ることができるわけです.
C言語とは違って,アセンブラには「型(タイプ)」という考え方はなく「このメモリエリアは整数2バイトで計算して,このエリアは実数なのでこっちのレジスタを使う.ここは4バイトのアドレスだから…」などなど,プログラマがつねに「何に使うのか」を決めて,その使い方を間違えないように正確にプログラミングしていたのです.
これを間違えるとバグになります.バグといっても当時のCPUやOSにはメモリ保護機能などありませんでしたから「バグ=暴走」です.暴走したらリセットをするしかなく,とくにプログラムをセーブしないで暴走してしまった場合は書いていたプログラムごと失ってしまうという,今では考えられないような人に厳しい環境なのでした.
このバグの原因となる型の間違いをチェックする単純作業を行うには,プログラムを作ってマシンにやらせたほうが正確だし高速だろうという考えから,多くのプログラミング言語の開発者は「タイプセーフ」という考え方に行き着いたようです.C言語やほかの多くの開発言語では,「多少不自由だけど安全を確保するためには型情報を記述してください」という形で,少しの不自由とひきかえにタイプセーフの考え方が選択されたのでした.
●床屋のカミソリ
どこかの本かネット上の文献に書いてあったと記憶しているのですが,タイプセーフは「床屋のカミソリと電気シェーバーの違い」の隠喩(いんゆ)が的を射ていると思います.この場合,床屋のカミソリがアセンブラで,電気シェーバーがC言語などのタイプセーフな言語となるでしょう.アセンブラでは最適なコードも書けますし,多少強引なコードでも融通が利きます.使用者の技術力がストレートに発揮されるわけです.しかし反面バグを出してしまう危険があります.その道を熟知したプロの道具なのです.
それに比べて,C言語などは安全性が高く,より多くの人が使えます.プロのような最適な効果を発揮することはできませんが,少ないリスクで手軽にそれなりの効果を発揮できるのです.
|
|
|
●●言語で考える |
|
|
「言語によって考えられることが決まってしまう」という恐ろしい言葉があります.身近な例で言うと「日本語で考えていると日本語にある微妙なニュアンスを認識し使い分けることができるが,日本語にない表現を理解することができない」ということです.おそらくどの言語であっても,ほかの言語とのギャップは存在すると思うのです.
プログラミング言語の世界であっても同じように,言語によって考えられることに制約を受けてしまうこともあるわけです.アセンブラ言語とほかの高級言語を比べて考えることで,その制約がどのようなものなのか理解してもらえると思います.C言語で考えられること,COBOLで考えられること,Lispで考えられること,Smalltalkで考えられること,それぞれ違うわけです.テンプレートやSTLを使う(理解する)ことでも,考えられることが変わってくると言いたいわけです.
|
|
|
●●C++で見つけたモノ−テンプレートとSTL |
|
|
あるとき突然「オブジェクト指向ブーム」がやってきます.そしてC++が標準化され,筆者も自然に(いやいやながら?)C++のほうへ流されるわけです.そんなオブジェクト指向ブームの中に,筆者はC++に何か光るモノを見つけたのでした.それがテンプレートでありSTLだったのです.テンプレートやSTLは直接オブジェクト指向とは関係がありません.世間がオブジェクト指向で沸き立つ中で,新しい小さくて静かなお店を見つけたような気持ちでした.店内に入ってみると,意外にも奥が深く居心地が良かったのです.
それは,今までのタイプセーフな言語では考えられない新しい概念を持っていました.型に依存しない「ジェネリックプログラミング」の考え方でした.
ここで少し疑問に思いませんか?そうですC++はタイプセーフな言語です.型を厳密にチェックする言語なのに,なぜ型に依存しないコードが書けるのでしょう.不思議に思いませんか?ちょっと不思議ですよね.
|
|
|
●●テンプレート |
|
|
ではここで,C++のテンプレートについて簡単に説明します.すでに熟知している方は「何でもテンプレート?」の項目まで読み飛ばしていただいて結構です.
●defineからテンプレートへ
C/C++にはdefine文があります.マクロとも呼ばれます.リスト1のマクロMAX()のようなコードは,プログラマであれば一度は見たことがあるでしょう.少し複雑なものだと,リスト2のようになります.これは,関数ふうのマクロTBLMAX()です.
マクロMAX()に関しては,引数の型がcharでもintでもdoubleでも正しく動きます.マクロTBLMAX()に関しても,char[]でもint[]でもdouble[]でも正しく動きます.サンプルコードをリスト3に示します.実行結果は,
9,9,5.900000
となります.
| リスト1 大きい値を返すコード(MAX()) |
#define MAX(A,B) (A>B?A:B)
|
| リスト2 配列の中で最も大きい値を返すコード(TBLMAX()) |
#define SIZE(TBL) (sizeof(TBL)/sizeof(TBL[0]))
#define TBLMAX(TBL,RESULT)\
{\
RESULT =TBL[0] ;\
for ( int i=1 ; i<SIZE(TBL) ; ++i )\
{\
RESULT = MAX( RESULT, TBL[i] ) ) ;\
}\
}
|
| リスト3 リスト2の使い方 |
//型の異なる配列
char c_tbl[] = "314159265358979" ;
int i_tbl[] = { 3, 1, 4, 1, 5, 9, 2, 6 } ;
double d_tbl[] = { 3.1, 4.1, 5.9, 2.6, 5.3 } ;
//答え
char c_result ;
int i_result ;
double d_result ;
TBLMAX( c_tbl, c_result ) ; // char
TBLMAX( i_tbl, i_result ) ; // int
TBLMAX( d_tbl, d_result ) ; // double
printf( "%c,%d,%f\n", c_result, i_result, d_result ) ;
|
それほど複雑なマクロではないのでとくに驚くことはないと思いますが,マクロには「型に対して融通がきく」という特徴があります.そこに着目してほしいのです.MAX()およびTBLMAX()どちらも関数のように振る舞うのに,マクロであるために型のチェックがない関数のように振る舞います.
それでもC/C++はタイプセーフな言語です.マクロが展開された後に,厳しく型のチェックが行われます.しかし,ソースコード上ではそこまで記述する必要はなく,あたかもすべての型の関数を定義したように振る舞います.ここです.この「型に厳しい言語なのに融通が利く」ところがテンプレートの目の付けどころだと思うのです.その点に筆者は「今までになかった新しい何か」を感じました.
では,MAX()およびTBLMAX()をC++のテンプレートを用いてリスト4のように書き直してみます.
機能や内容はdefine版と同じです.ただ,C/C++の関数では引数に配列が渡せないので配列のサイズ情報も引数に渡す必要がありました(C/C++って微妙に不便ですね).define版では,コードが複数行にわたる場合,改行の前に\(エンサイン)が必要なのですが,見た目にも記述作業としてもうっとうしいのです.テンプレート版ではそれが必要なくなり,すっきりしています.このテンプレート版TTblMax()の使い方はリスト5のようになります.
define版TBLMAX()と同じように,普通の関数のように使えます.
リスト4 テンプレートを使ったMAX()と
TBLMAX()の書き換え(TTblMax()) |
//大きい値を返す(テンプレート版)
template< typename _T >
_T TMax( _T a, _T b )
{
return a >b ? a : b ;
}
//配列の中でもっとも大きい値を返す(テンプレート版)
template<typename _T >
_T TTblMax( _T *tbl, int size ) // げげ,配列サイズがいるのか^^;
{
_T result = tbl[0] ;
for ( int i=1 ; i<size ; ++i )
{
result = TMax( result, tbl[i] ) ;
}
return result ;
}
|
| リスト5 リスト4の使い方 |
char c_result = TTblMax( c_tbl, SIZE( c_tbl ) ) ;
int i_result = TTblMax( i_tbl, SIZE( i_tbl ) ) ;
double d_result = TTblMax( d_tbl, SIZE( d_tbl ) ) ;
printf( "%c,%d,%f\n", c_result, i_result, d_result ) ;
|
●型に依存しないクラス
さて,ここでもう一歩進んでみましょう.defineを使って「型に依存しない関数」が作れるのなら,同じように「型に依存しないクラス」は作れないのか,という話です.そこで,リスト6のようにTBLMAX()と同じ機能を持つクラスを作るマクロを書いてみます.
使い方は,テンプレート版関数TTblMax()とほぼ同じです(リスト7).ただし,あらかじめ必要となる型を宣言しておく必要があります.実行結果は次のようになります.
9,9,5.900000
ではこのdefine版C_TBL_MAX()をテンプレートに書き直してリスト8のようにします(CMax<>).
こちらもかなりすっきりしました.やっていることはC_TBL_MAX()とまったく同じです.CMax<>の使い方はリスト9のようになります.この場合,必要となる型であらかじめ宣言しておく必要もありません.define版に比べてだいぶ使いやすくなっています.
| リスト6 最大値を覚えているクラス |
#define C_TBL_MAX(TYPE)\
class C_MAX_##TYPE \
{\
public:\
TYPE m_result ;\
C_MAX_##TYPE( TYPE * in, int size )\
{\
m_result = in[0] ;\
for ( int i=1 ; i<size ; ++i )\
{\
m_result = TMax( m_result, in[i] ) ;\
}\
}\
}
//必要な型のぶんだけ宣言する
C_TBL_MAX( char ) ;
C_TBL_MAX( int ) ;
C_TBL_MAX( double ) ;
|
| リスト7 リスト6の使い方 |
C_MAX_char max_c( c_tbl, SIZE( c_tbl ) ) ;
C_MAX_int max_i( i_tbl, SIZE( i_tbl ) ) ;
C_MAX_double max_d( d_tbl, SIZE( d_tbl ) ) ;
printf( "%c,%d,%f\n",
max_c.m_result, max_i.m_result, max_d.m_result
) ;
|
| リスト8 最大値を覚えているクラスのテンプレート版 |
template< typename _T >
class CMax
{
public:
_T m_result ;
CMax( _T * in, int size )
{
m_result = in[0] ;
for ( int i=1 ; i<size ; ++i )
{
m_result = TMax( m_result, in[i] ) ;
}
}
} ;
|
| リスト9 リスト8の使い方 |
CMax< char > max_c( c_tbl, SIZE( c_tbl ) ) ;
CMax< int > max_i( i_tbl, SIZE( i_tbl ) ) ;
CMax< double > max_d( d_tbl, SIZE( d_tbl ) ) ;
printf( "%c,%d,%f\n",
max_c.m_result, max_i.m_result, max_d.m_result
) ;
|
ここまでの説明でdefineとテンプレートの関係が見えてきたと思います.テンプレートというと難しく考えてしまいがちですが,「新しいマクロ」だと思えばそれほど難しくはないと思います.そして,テンプレートは「型に依存しないコードを書くときに使う」と覚えておけば良いでしょう.
安全ならと「型」という不自由を覚悟して入った世界なのに,いざ不自由に浸かるとやはり「型」のない自由な世界を求めてしまうようで,やはり人間とは勝手な生き物のようですね.
●何でもテンプレート?
では,すべてのコードをテンプレートを使って「型に依存しないジェネリックなコード」にすべきでしょうか.筆者はテンプレートを使ったコードをいくつか書いてみましたが,あまりメリットを感じ取ることはできませんでした.再利用されるのかわからないコードに手間をかけて再利用できる形にする作業や,オブジェクト指向を適用する必要もないような小さなコードに対してオブジェクト指向を適用している作業と同じように思えたのです.
一言で言ってしまえば,それは「ゴージャスなコード」となるでしょう.いや,決して再利用やオブジェクト指向やテンプレート(ジェネリック)なコードは書く必要がないと言っているのではなくて,やはり戦術の1つであると認識し,必要に応じて適用すべきだと思うのです.
|
|
|
●STL |
|
|
C++にテンプレートがサポートされたことに関しては,それほど大きなインパクトがあったわけではないと思います(C++コンパイラの開発者には大きなインパクトだったとは思いますが).ところがこのテンプレートのサポートによって,STL(標準テンプレートライブラリ)というライブラリもサポートされました.実はこいつがとんでもないヤツだったのです.
皆さんがSTLというとまず思い浮かべるコードは
cout << "Hello\n" ;
のようなコードではないでしょうか.coutは立派なSTLのライブラリです.しかし,coutがSTLの本当の姿ではなかったのです.本当に凄かったのは,これから説明するvector<>やstringなどのコンテナクラスやイテレータや関数オブジェクトの概念だったのです.
●型に依存しないライブラリ
STLを整理して考えると,次の4つに分けることができます.
@ コンテナクラス
A イテレータ(反復子)
B アルゴリズム
C 関数オブジェクト
どれも重要な概念です.テンプレートの機能は「型に依存しないコードが書けるようになる」程度のインパクトでした.しかし,その型に依存しないコードを使ってSTLは「アルゴリズム」や「パターン」をライブラリ化できるという新しい扉を開いたのです.
●@コンテナクラス
STLの中でもっとも容易に効果を発揮するのが,このコンテナクラス群だと思います.コンテナクラスの数も数種類と少ないですし,苦労せずにこれらのメリットを使いこなせると思います.おもなコンテナクラスを表1に示します.

この中で最もよく使うのが,vector<>とstringだと思います.まずは,この2つから使ってみると良いでしょう.
●vector<>
vector<>のメリットは配列と互換性があり,リスト10のようなコードでも問題なく通ることです.&buff[0]が配列のポインタと同じ動きをするところがミソです.しかも動的配列の実装なので,push_back()やresize(),reserve()などの強力なメソッドが売りなのです.これらのメソッドの詳細については各自で調べてください.
| リスト10 vector<>の例 |
#include <vector> // std::vector
#include <stdio.h> // FILE, fopen()
int main()
{
const int buff_max = 32 ;
std::vector< unsigned char > buff( buff_max );
//unsigned char buff [ buff__max ] ;と同じように使える
FILE * fp = fopen( "hello.cxx", "rb" ) ;
if ( fp != NULL )
{
for ( ; ; )
{
int size = fread( &buff[0], 1, buff_max-1, fp ) ;
if ( size == 0 ) break ;
buff[size] = '\0' ;
printf( "%s", &buff[0] ) ) ;
}
fclose( fp ) ;
}
return 0 ;
}
|
●string
stringは,コンテナクラスというよりは文字列に特化した文字列専用のクラスだと言って良いでしょう.stringはリスト11のようなコードが書けます.
| リスト11 stringの例 |
#include <string> // std::string
#include <stdio.h> // FILE,fopen()
int main()
{
std::string str ;
FILE * fp = fopen( "hello.cxx", "r" ) ;
if ( fp != NULL )
{
for ( ; ; )
{
char buff[256] ;
char * rslt = fgets( &buff[0], 256, fp ) ;
if ( rslt == NULL ) break ;
str += buff ; // ここがミソ
}
fclose( fp ) ;
}
printf( "%s", str.c_str() ) ;
return 0 ;
} |
stringはvector<>とは違ってstr.c_str()のエリアに直接書き込みはできませんが,“==”などの比較演算子や代入系の演算子などが用意され,至れり尽くせりなところが魅力です.
vector<>と同様に,動的にメモリの確保を行ってくれますので,気にせずにどんどん放り込めます.
また,デストラクタでしっかりとメモリを開放してくれますので,メモリリークの心配もありません.
これらのコンテナクラスを使えば,malloc()やfree()などの可読性が低くメモリリークのしやすい危険なコードを書かずに済むのです.
●Aイテレータ(反復子)
次は「イテレータ」です.イテレータを簡単に説明すると,「配列のポインタのような振る舞いをするオブジェクト」になるでしょうか.もっと簡単に説明すると「operator*とoperator++を持ったオブジェクト」となるでしょう.イテレータとは,単にポインタのようなインターフェースを持ったオブジェクトとして考えられてしまいがち(実際にその程度のモノ)ですが,筆者はこのポインタのようなインターフェースと使い方のパターンを「定義したところ」がイテレータの凄さだと思うのです.
少しわかり難いかもしれませんので整理して説明ます.今までは「ポインタ」がありました.これは「何かを指し示すモノ」として定義され,より具体的には「アドレスが使われる」とされてきました.
イテレータは何かを指し示すと同時に「次」という概念も持っています.当然ポインタも「次」という概念を持っていますが,こちらは単純に「型のサイズぶんの次のアドレス」という意味でした.配列のポインタであれば「次」は意味を持つのですが,配列以外を指すポインタは「次」の意味はないということになります.

イメージを図1に示します.イテレータはポインタの概念を抽象化し「operator*とoperator++のインターフェースを守ってください.そして,指し示すモノがなくなったら教えてください.細かい実装についてはお任せします」と,これだけの単純な概念にまとめたわけです.
ポインタという概念では,「配列を指すポインタ」しかイテレータの概念に入りませんが,*や++を実装したオブジェクトであれば,その指し示すモノが配列の中身である必要はなくなったということなのです.vector<>であってもlist<>であっても,どんなコンテナクラスでもストリームでも良いのです.
「で,何が凄いの?」という声が聞こえてきそうなので,さらに補足します.皆さんは,他人の車を借りて運転することはできますか?同じインターフェースなので,さほど苦労をせずに使うことができると思います.逆に,WindowsからLinuxに移ると,同じDOS/Vパソコンであるにも関わらずインターフェースの違いで苦労させられたりすると思います.そういうことなのです.
イテレータは配列のポインタを抽象化したのです.インターフェースを決め,使い方を決め,共通のパターンにしたわけです.イテレータはイテレータなのです.イテレータと言われたら「こういう使い方ができる」「こう使えばいいんだ」と直感的に想像できるし,そのための使い方が統一され,コードが理解しやすくなる方向へ向かうのです.そして,このイテレータで使い方が統一されることによって,次に説明するアルゴリズムや関数オブジェクトが活きてくるのです.
インターフェースを決めるだけのことなら,オブジェクト指向にも存在していた考え方です.しかし,これがテンプレートによって加速するわけです.インターフェースとテンプレートを組み合わせることで双方の良いところが引き出されるわけです.
●Bアルゴリズム
STLにはたくさんのアルゴリズムがあります.その中で一番多く使うアルゴリズムは,for_each(),copy(),sort(),find()あたりでしょうか.簡単に解説するには,やはり,C/C++の関数のqsort()とSTLのsort()を比較するのが一番良いでしょう(リスト13).実行結果は,
112233334455566788999
112233334455566788999
のようになります.
| リスト13 qsort()とsort()の比較 |
#include <algorithm> // std::sort()
#include <stdlib.h> // qsort()
#include <stdio.h> // puts()
int qsort_compare( const void * elem1, const void * elem2 )
{
const unsigned char * c1 = (const unsigned char *)elem1 ;
const unsigned char * c2 = (const unsigned char *)elem2 ;
return ( *c1 >*c2 ) ? 1 : -1 ;
}
int main()
{
// 関数qsort()の実装例
{
char str [] = "314159265358979323846";
qsort(
(void *)str, // ソートする配列の先頭
sizeof(str)-1, // 配列の要素数
sizeof(str[0]), // 配列要素のサイズ(バイト数)
qsort_compare // 比較関数
) ;
puts( str ) ;
}
// STL std::sort()の実装例
{
char str [] = "314159265358979323846";
std::sort(
&str[0], // 先頭
&str[sizeof(str)-1] // 最後
) ;
puts( str ) ;
}
return 0 ;
}
|
qsort()は関数なので,型が合っていないとエラーになります.そのためvoid*やconst
unsigned char*などにキャスト(型変換)する必要があります.
一方,STLのsort()ではキャスト(型変換)する必要はありません.テンプレートのおかげです.また比較用の関数qsort_compare()を用意する必要もありません.STLはインラインに展開されますので,qsort()とは違って最適化がかかりやすく,プログラムサイズや実行速度にも有利なのではないかと言われています.実際にはケースバイケースなので実測して確かめてみるのが良いと思います.
今回,sort()の引数に配列のアドレスを渡していますが,STLのマニュアルにはイテレータを渡すように書いてあります.配列のアドレスもイテレータの一種として扱うことができるということです.もちろんvector<>などのイテレータも入ります.
キャスト(型変換)はgoto文以上に書くべきではないコードであると筆者は思っています.goto文であればまだ必要となる状況がいくつか想像できますが,キャストにはメリットとなる状況が思い浮かびません.とくにダウンキャストは危険だと思います.キャストはタイプセーフという言語の性質から出てきた抜け道というか万能のカードのようにも思えます.そのカードが使えるのは便利かもしれませんが,ではそのカードの使い方を誤った場合は誰が責任をとるのでしょうか.
テンプレートを使えばキャストを使わずにコードを書けるようになります.キャストが必要になった場合はテンプレートで書きなおすことができないか,一度考えてみると良いでしょう.
●C関数オブジェクト
関数オブジェクトもSTLにたくさん用意されています.よく使うのはless<>やgreater<>でしょう.
関数オブジェクトは用意されているモノを使うよりも,自作のクラスに適用した関数オブジェクトをアルゴリズムに合わせて用意するといった使い方が多いのではないでしょうか.
関数オブジェクトを簡単に説明すると「関数ポインタのような振る舞いをするオブジェクト」となるでしょうか.もっと簡単に説明すると「operator()を持ったオブジェクト」となるでしょう.
関数のポインタとの違いは,オブジェクトであるためメンバを持てることと,そのオブジェクト自身いくつも作れる(同時に複数存在できる)ということでしょう.関数のポインタの場合は,関数は1つしか存在できません.
関数オブジェクトを使うことで,コールバックルーチンのような機能が実現できます.アルゴリズム内の一部の機能を切り出してあとから実装できるようになるわけです.
筆者はあまり関数オブジェクトを好きでありません.なぜなら,ほんの2〜3行の処理をさせるためにクラスを書かなければいけないことと,そのコードは処理しているコードから遠く離れたところ(クラスの外)に書かなければならないという2つの理由からです.だからといってこんなに便利な機能を捨てるなどということはしたくなく,つねにもどかしさを感じます.次のC++の言語仕様が良い方向に変わってくれることを祈るだけです.
●STLのメリット
STLのメリットは保守性が上がることだと思います.繰り返し使われるようなsort()やfind()などのアルゴリズムを,自作のコードではなく標準のライブラリを使うことで「誰が書いても同じような書き方方になる=可読性/保守性が良くなる」ことだと思うのです.ですから,新しいコンテナクラスを作ろう!とか,自作のイテレータを作ろう!という方向へのアプローチはSTLのメリットある利用とは思えません.たしかに,実際に標準ではない優秀なテンプレートライブラリ(LokiやBoostなど)はいくつか存在します.しかし,多くの人が共有するようなコードには,自作のコンテナや自作のアルゴリズムや標準ではないテンプレートライブラリを入れるのは,保守などを考えるとお勧めできません.
STLは「STLを使うことで書き方が共通になる」ところがおもなメリットなのです.自作のコンテナや標準以外のテンプレートライブラリを使う前に,保守や可読性のことを考えてみてください.
●STLの問題点
最後に,STLの問題点を挙げてみます.
@ コンパイル時のエラーメッセージが難解
→コンパイラが賢くなるように祈りましょう.
A デバッガで追うことが困難
→正しく動作する記述パターン(定石)を覚えることで回避しましょう
B テンプレートの構文は読みにくい
→typedefなどを使って短くて適切な名前に書き換えていくしかありません
C 実行コードが肥大化し実行速度が遅くなる
→コンパイラが賢くなるように祈りましょう
STLであっても万能ではないということですが,これらのデメリットよりも多くのメリットが得られるので,積極的に使っていきましょう.
|
|
|
◆◆◆ |
|
|
次回は「デザインパターン」について書いてみたいと思います.お楽しみに.
|
|
|
この原稿を本にしてくれる出版社さんを募集しています。英語とか韓国語、中国語での出版も希望しています。よろしくお願いします。
vol.01 vol.02 vol.03 vol.04 vol.05 vol.06 vol.07 vol.08 vol.09
vol.10 vol.11
vol.12 vol.13 ML
|
| やまざきのおすすめエレクトロニクス |
|
| やまざきのおすすめ本 |
|
| やまざきのおすすめDVD |
|
| やまざきのおすすめCD |
|
|