C++11の機能 (型推論, 範囲for)
C++11は2011年に策定された規格でだいぶ経つ仕様ですが、今でも頑張ってこのバージョンしか無理みたいな環境があります。
組み込みとかCentOSとか
そういう環境を使っている人向けに便利だなーと思う機能をいくつか紹介していきたいと思います。
前者の型推論を持つ言語としてはC#, Dart, Kotlin, Java(10以上)などがあります。
後者の型推論を持つ言語としてはJavaがあり、ダイアモンド演算子と呼ばれています。こんな機能にどんな意味があるんだとか言われていましたがないよりはあったほうが圧倒的に便利です。
C++は前者のパターンの型推論です。
C++の型推論は次のように書きます
この機能はC++としては珍しく破壊的変更が行われた機能です。
どういうことかというとautoは自動変数(ローカル変数)という意味の記憶域クラス指定子でした。
ですが、これってあんまり使われて無くねということになりautoは自動なので自動的に型が決定することになぞらえたのか型推論を表す予約語になりました。
上記の例はintに推論されます。
intだとあんまり便利さを感じません(autoのほうが長いですし)が次のような型ではめちゃくちゃ便利だと思います。
この例はunordered_mapという型のイテレータですが随分長ったらしい型がautoの4文字になっています。
こんな感じでコード量を小さくできるのが魅力です。
C++20からRangesライブラリが入りますがそれの先触れのような機能で、イテレータで範囲が表せるコンテナの走査ができます。
のように書きます。
ここで少し脱線しますが、C++11からstd::beginとstd::endという非メンバ関数が追加されました。
この関数はそれぞれ配列やコンテナオブジェクトの先頭を指すイテレータと末尾+1を指すイテレータを返します。
この関数の良いところは配列でもコンテナオブジェクトでも同じようにイテレータが取れることです。
話は戻り、範囲forで回せるオブジェクトの種類について説明します。
範囲forで回せるのはイテレータを持つオブジェクトか配列です。
イテレータをどのように見つけるかはstd名前空間を探索対象に入れたADLでbegin, endが探せればOKです。
つまり、配列は普通に先頭と末尾はわかるので大丈夫だと思いますが、コンテナはまずcontainer.begin()とcontainer.end()が呼べるかを見て呼べるのであればそれをそのままイテレータとして使います。呼べないならbegin, endをADLで探します。
以上のことを踏まえC++11の段階の範囲forを普通のforで書いてみます。
__で始まる名前は説明用の名前なので実際にこのようには展開されません。
using std::beginの部分も説明用なので実際にこのように展開されません。
C++11の段階と前置きした理由は先頭と末尾のイテレータは同じ型でないといけないという制約があるからです。この制約はC++17以降では無くなっています。
組み込みとかCentOSとか
そういう環境を使っている人向けに便利だなーと思う機能をいくつか紹介していきたいと思います。
型推論
型推論は一般に右辺の値から左辺の新しい変数の決定したり、逆に左辺の型から右辺の型の一部を補完したりという、要するに型をいちいち書かなくても良くする機能です。前者の型推論を持つ言語としてはC#, Dart, Kotlin, Java(10以上)などがあります。
後者の型推論を持つ言語としてはJavaがあり、ダイアモンド演算子と呼ばれています。こんな機能にどんな意味があるんだとか言われていましたがないよりはあったほうが圧倒的に便利です。
C++は前者のパターンの型推論です。
C++の型推論は次のように書きます
auto number = 1;
この機能はC++としては珍しく破壊的変更が行われた機能です。
どういうことかというとautoは自動変数(ローカル変数)という意味の記憶域クラス指定子でした。
ですが、これってあんまり使われて無くねということになりautoは自動なので自動的に型が決定することになぞらえたのか型推論を表す予約語になりました。
上記の例はintに推論されます。
intだとあんまり便利さを感じません(autoのほうが長いですし)が次のような型ではめちゃくちゃ便利だと思います。
std::unordered_map<std::string, std::vector<std::string>>::const_iterator itr = ...; auto itr = ...;
この例はunordered_mapという型のイテレータですが随分長ったらしい型がautoの4文字になっています。
こんな感じでコード量を小さくできるのが魅力です。
範囲for (Range-based for loop)
範囲forはいわゆるforeachに当たるループです。C++20からRangesライブラリが入りますがそれの先触れのような機能で、イテレータで範囲が表せるコンテナの走査ができます。
std::vector<std::string> container = {0, 1, 2, 3, 4}; for (const auto& e : container) { std::cout << e << std::endl; }
のように書きます。
ここで少し脱線しますが、C++11からstd::beginとstd::endという非メンバ関数が追加されました。
この関数はそれぞれ配列やコンテナオブジェクトの先頭を指すイテレータと末尾+1を指すイテレータを返します。
この関数の良いところは配列でもコンテナオブジェクトでも同じようにイテレータが取れることです。
話は戻り、範囲forで回せるオブジェクトの種類について説明します。
範囲forで回せるのはイテレータを持つオブジェクトか配列です。
イテレータをどのように見つけるかはstd名前空間を探索対象に入れたADLでbegin, endが探せればOKです。
つまり、配列は普通に先頭と末尾はわかるので大丈夫だと思いますが、コンテナはまずcontainer.begin()とcontainer.end()が呼べるかを見て呼べるのであればそれをそのままイテレータとして使います。呼べないならbegin, endをADLで探します。
以上のことを踏まえC++11の段階の範囲forを普通のforで書いてみます。
using std::begin; using std::end; for (auto __first = begin(container), __last = end(container); __first != __last; ++__first) { auto e = *__first; std::cout << e << std::endl; }
__で始まる名前は説明用の名前なので実際にこのようには展開されません。
using std::beginの部分も説明用なので実際にこのように展開されません。
C++11の段階と前置きした理由は先頭と末尾のイテレータは同じ型でないといけないという制約があるからです。この制約はC++17以降では無くなっています。
コメント
コメントを投稿