どうも執筆がはかどらないので、気分転換に、C++0xの新機能によって、一般ユーザーのコードがどのように便利になるかを示してみようと思う。
問題:以下のような仕様の関数printを実装し、また実際に呼び出しなさい。
宣言:関数printは、std::vector< std::string > const &を仮引数に取り、std::vector< std::string >::const_iteratorを戻り値の型として返す。
本体:関数printは、仮引数のvectorの要素をすべて標準出力に出力する。
戻り値:v.cbegin()。
C++03の範囲(ただし、cbegin、cendあり)でこれを実装すると、以下のようになる。
std::vector< std::string >::const_iterator print( std::vector< std::string > const & v ) { for ( std::vector< std::string >::const_iterator iter = v.cbegin(), end = v.cend() ; iter != end ; ++iter ) { std::cout << *iter << std::endl ; } return v.cbegin() ; } int main () { std::vector< std::string > v ; v.push_back("foo") ; v.push_back("bar") ; v.push_back("hoge") ; v.push_back("moke") ; print( v ) ; }
このコードは、何をしているのかさっぱりわからない。おそらく、一ヶ月後にこのコードを見なおしても、書いた本人ですら、まともに読めないだろう。C++0xの新機能を使って、このコードを読めるようにしてみよう。
まず問題なのは、std::vector< std::string >::const_iteratorなどという、べらぼうに長い型名だ。これを何とかしよう。それには、auto specifierとdecltypeが使える。
auto specifierは、変数の型を、初期化子から決定してくれる、大変便利な新機能である。decltypeは、型をdecltypeに渡した未評価式の型にしてくれる、これまた便利な新機能である。
// 新しい関数記法 auto print( std::vector< std::string > const & v ) -> decltype( v.cbegin() ) // decltype { // auto for ( auto iter = v.cbegin(), end = v.cend() ; iter != end ; ++iter ) { std::cout << *iter << std::endl ; } return v.cbegin() ; }
だいぶマシになった。次は、for文だ。わざわざループのためのイテレーターのインクリメントや、終了条件の判定を、自分で書くのは面倒だし、間違いのもとだ。これには、range-based forが使える。
auto print( std::vector< std::string > const & v ) -> decltype( v.cbegin() ) { for ( auto elem : v ) { std::cout << elem << std::endl ; } return v.cbegin() ; }
これで、関数printは、実に短く簡潔に書ける。これで、一年後にコードを読んでも、スラスラと読めるだろう。
ところで、このprint関数を呼び出すために、vectorを用意するのが、これまた面倒だ。
std::vector< std::string > v ; v.push_back("foo") ; v.push_back("bar") ; v.push_back("hoge") ; v.push_back("moke") ;
これには、initializer listが使える。
int main () { std::vector< std::string > v = { "foo", "bar", "hoge", "moke" } ; print( v ) ; }
これも、POD型のクラスの初期化と同じで、非常に分かりやすい。
流石に
ReplyDeleteprint( std::vector< std::string >( "foo", "bar", "hoge", "moke" ) ) ;
とか
print( std::vector< std::string >{ "foo", "bar", "hoge", "moke" } ) ;
的なことは出来ないですよね?
後者はできます。
ReplyDelete仮引数の型が、std::vector< std::string >、もしくはそのconst lvalueリファレンスかrvalueリファレンス型だとして、
print( std::vector< std::string >{ "foo", "bar", "hoge", "moke" } ) ;
や、
print( std::vector< std::string >({ "foo", "bar", "hoge", "moke" }) ) ;
はできます。