Library for libraries

C++は何を間違えたのか: 人生を書き換える者すらいた。より.

2009/12 のBoost.勉強会で一番記憶に残ってるのが,id:faith_and_brave がプレゼン中に何度か使っていた「Library for libaries」と言う言葉でした.この言葉には,Boost などが提供するライブラリには「“エンドユーザ”(アプリケーションを作成するプログラマ)は,別に知らなくても使えなくても問題のないライブラリが数多く存在する」と言う意味も含まれています.

いくつか言語を触りましたが,個人的な主観では今でも C++ が書いていて一番心地良いです.これは,私がライブラリ脳 だからと言う事もあります.C++ は他言語よりも「うまくライブラリを書く」ための機能やライブラリが充実しているように感じます.例えば,他言語だと実行時に型チェックをして型毎に必要な処理を記述するようなものが,テンプレートと Boost.Range, Boost.MPL のようなライブラリを使用する事によってコンパイル時にうまく分岐させる事ができたりします.

しかし,そう言った多用途のライブラリを書くのではなく,アプリケーションなどのように想定する動きがある程度決まっているものをプログラムする際には,こう言った機能やライブラリは必ずしも必要ではありません.

テンプレートクラスを書く事と STL を使う事の違い

STL を使いこなす為にはテンプレートへの理解が必要不可欠である」のような事をたまに耳にします.「使いこなす」がどのレベルを指すのか分からないので明言はできませんが,STL を使うだけであれば別にテンプレートへの理解はそこまで必要ありません.例えば,iostream や string はテンプレートクラスですが,“エンドユーザ”は別にそれがテンプレートクラスと言う事を認識していなくても問題ありません.STL を使う上で必要な知識は,恐らくは,各種コンテナで「<>の中に自分が使いたい型を記述する」と言う事とイテレータの使い方くらいかなと思います.

Amazon CAPTCHA と言う本がありますが,「自分でテンプレートクラスを書く」と言う観点から見ると,この本を読んでもテンプレートクラスをうまく書けるようになる訳ではありません.逆に,テンプレートクラスを書く上では Amazon CAPTCHAAmazon CAPTCHA などの本は大きな助けとなりますが,STL を使う上では必ずしも読む必要はありません*1

このように「テンプレートクラスを書く事」と STL などのように「テンプレートクラスで書かれたクラスを使う事」の間には,ギャップが存在します.

MoveConstructor と std::move()

新仕様のc++0xの解説もちょっとあるが、いよいよ変態の領域にきている。ムーブコンストラクタなぞとても使いこなせる自信がない。

C++は何を間違えたのか: 人生を書き換える者すらいた。

MoveConstructor は,私自身もうまく定義できる自信はありません.恐らく,何年もの月日を経て定石と呼ばれる手法が確立して初めてまともに使いこなせるようになるのではないかと予想しています.

ただ,“エンドユーザ”は MoveConstructor の存在など気にせず,今まで通りプログラミングすれば問題ありません.MoveConstructor は,(主に?)長年言われ続けてきた「C++ はコピー回数(コピーコンストラクタが実行される回数)が多すぎる」と言う問題への解決のために導入されました.

例えば,STL コンテナに要素を追加する際には,その度にコピーが発生します.これまで,このコピーによるオーバーヘッドや危険性を回避するために,「ある型へのポインタのコンテナ」を使用すると言う場面がしばしば見られました.しかし,この使い方によって予期せぬバグを埋め込むと言う場合も多々存在しました.MoveConstructor は,こういったトリッキーな使い方をできるだけ正常な形に戻すためのものです.

“エンドユーザ”は,MoveConstructor を理解する必要はありません.MoveConstructor は,気にしなくても出来る限り無駄なコピーが減るように,STL や Boost などのライブラリ作成者が頑張るための機能と言う位置づけになります.“エンドユーザ”にとっては,どちらかと言えば,必要とされる知識は 「std::move() の使い方」だろうと思います.上記の例で言うと,「ポインタ型のコンテナを使う代わりに,一時変数などを通してコンテナへの要素へ追加する際に std::move() を使う」と言う事になります.

std::move()についてだ。ワシは、別に実装など説明しなくても、ムーブを説明できるのだ。Matrixの例で取り上げた、ムーブセマンティクスを考えてみたまえ。コンパイラは、Matrixオブジェクトを返すとき、コピーの代わりにムーブが使えるということを知っている。なぜならば、コンパイラは、関数からreturnした後はローカル変数を使うことがないということを知っているからだ。時にプログラマは、ある値が、もうこれ以上使われないということを保証できる。しかし、コンパイラはそのことを知る手段がない。その場合、プログラマコンパイラに、コピーではなくムーブを使って欲しい。プログラマは、std::move()を使うことによって、コンパイラに、ムーブが使えるということを伝えられるのだ。

プログラミングの魔導書 / Vol.1 Construct the World, C++

必要なものだけ理解すると言う態度

C++ は C との互換性の兼ね合いなどの関係もあって,扱いの難しい言語です.しかし,必要以上に C++ を難しいと感じてしまう原因の一つとして「全てを理解しようとする態度」があるような気もします.

現代のソフトウェアというものは、想像もできないほど複雑だ。ハードウェアについては言わずもがなだ。言語のあらゆる機能のあらゆる詳細を、完全に理解しようとするのは馬鹿げたことだ。結局、すべてを理解することはできんのだし、そもそも、すべてを理解したいとは思わん。

・・・(中略)・・・

プログラミング言語というのは、必要なだけ知っておけば良い。すべてを極めようとするのは非生産的だ。ワシですら、本や標準規格等といった補助がなければ、C++のあらゆる疑問に答えることはできん。物事をすべて暗記しようとすれば、プログラマとしては使い物にならなくなるだろう。覚えて置く必要があるのは、もっと高級な、C++の概念だけでいい。

プログラマが言語について知っておくべきなのは、基本的な機能だ。主要な機能の基本的な仕組みだ。そして、必要に応じてより高度なことを学ぶための方法だ。いうなれば、プログラマに必要なものは、言語の理念と情報だ。おまじないなどを信じる必要はない。C++には、他の言語より「魔法」が少ない。むしろ、これが問題なのだろう。標準ライブラリのアルゴリズムであるとか、Boostライブラリが、どのように書かれているのか、覗き見ることができてしまう。これらのコードを読むのは、時として非常に高度な能力が要求される。これが、人々をして、震え上がらせるのだ。JavaC#といった言語ではそういったことはない。実装の詳細は、OS側などに隠されていて、普通のユーザーはまず覗き見ることがないのだ。

プログラミングの魔導書 / Vol.1 Construct the World, C++

Stroustrup 博士のこの言葉は,プログラミングの魔導書 / Vol.1 Construct the World, C++の中でもかなり印象に残っているものの一つです.

*1:どちらも程度の問題で,もちろんある程度は役に立つ.