ダウンロード サンプルファイル (2.3 KB)
スコープ付き列挙型[C++11]
列挙型(enum)はC言語ですでに使えるようになっていた言語仕様で、マジックナンバー(直にコーディングされた数値など)を嫌うプログラマが好んで使っていたように思えます(筆者です)。なんてことのない定数値でも、わざわざ名前を割り当てて使うという感じです。「enum Boolean {False = 0, True}」みたいな感じですね。
従来からの列挙型
とはいえ、マジックナンバーはコードの可読性を下げますし、値と意味の重複が起きる可能性があるので、列挙型で置き換えるのは妥当ですね。例えば、以下のリストです。
enum Trump {Spade, Heart, Club, Diamond};
auto card = Spade; // 0
このとき、列挙型Trumpの各メンバには、0からの整数値が順番に割り振られます(メンバ名に「=」を続けて値を明示的に指定もできました)。なので、cardを出力させると0となります。ちなみに、トランプは英語にするとPlaying Cardsです。分かりやすいようにTrumpとしただけですので誤解しないでくださいね。
また、同じメンバを持つ列挙型、例えばTrump2を宣言しようとしても、再定義のエラーとなります。列挙型間でメンバが重複しないように、細心の注意を払う必要があるわけですね。筆者などは、各メンバに列挙型の名前をプレフィクスで付けるとか、今となっては冗長なことをしていました。
スコープ付き列挙型
そんなことで、C++ 11において「スコープ付き列挙型」(scoped enumeration type)が導入されました。スコープ付き列挙型では、列挙型名によってメンバ名がスコープされるので、列挙型間でメンバ名の重複が起きません。想像しただけですごく便利そうですね。なお、スコープ付き列挙型の導入に合わせて、従来の列挙型は「スコープのない列挙型」(unscoped enumeration type)として区別されることになりました。
スコープ付き列挙型は、以下のリストのようにenum classあるいはenum struct構文で定義します(enum classとenum structは同じ働きをします)。
enum Trump {Spade, Heart, Club, Diamond};
enum class Jewel {Garnet = 1, Amethyst, Aquamarine, Diamond};
auto birth_jewel = Jewel::Diamond;
cout << static_cast<int>(birth_jewel) << endl; // 実行結果:4
メンバDiamondはスコープのない列挙型Trumpとスコープ付き列挙型Jewelで被っていますが、再定義エラーにはなりません。その替わり、メンバにアクセスする際にはJewel::Diamondのように列挙型名でスコープ演算子(::)を使う必要があります。また、スコープのない列挙型のようにメンバに明示的な値の指定も可能です。
スコープ付き列挙型では、メンバに対して暗黙の型変換を実施しません。実際の値を取り出すには、上記リストのようにstatic_cast演算子を使っての明示的な型変換が必要です。
基底型を指定できる
スコープ付き列挙型では、各メンバのデータ型(基底型)を指定できます。基底型に指定できるのは数値型のみなので、メモリレイアウトを考慮しているということなのでしょう。基底型は、以下のリストのように列挙型名にコロン(:)を続けてそのあとに指定します。なお、スコープ付き列挙型における既定の基底型は「int」です。
enum class Jewel2: char {Garnet, Amethyst, Aquamarine, Diamond};
auto birth_jewel2 = Jewel2::Diamond;
cout << +static_cast<underlying_type<Jewel2>::type>(birth_jewel2) << endl;
この場合、birth_jewel2はchar型を基底型に持ったメンバということになります。実際の値は、既述のように静的キャストで取り出すことができますが、ここでstd::underlying_typeで実際の型を取り出して、それを指定することができます。わざわざ何度も同じ型名を書かなくて済みますし、基底型を変更したときにも楽ですね。
なお、これに合わせて従来の列挙型でも、基底型を指定できるようになりました。
イテレータと範囲for
配列をはじめとするコレクションには、必ずと言ってよいほど繰り返し処理、要素の走査といった処理が付きものです。そのため、モダンなプログラミング言語には「イテレータ」(反復子)という仕組みが備わっていて、これを簡便なものにしてくれていることがほとんどです。我らがC++言語にもイテレータが存在しましたが、それはイテレータとは似て非なるもの、という感が強かった記憶があります。このイテレータがどういうものだったのかというあたりから見ていきましょう。
イテレータ構文
C++におけるイテレータとは、コンテナの各要素を指すポインタです。例えばstd::vectorといったコンテナにおいて、その先頭要素(begin())や最終要素(end())を指すイテレータを取得し、範囲内で要素にアクセスするといった使い方が基本です。これを使って繰り返しをするのが以下のリストです。
#include <vector>
std::vector<int> x = {0, 1, 2, 3, 4};
for(auto it = x.begin(); it != x.end(); it++)
{
cout << *it << endl;
}
イテレータと称していますが、C/C++伝統のfor文を使って、地道にループを回しているだけです。分かりやすいといえば分かりやすいですね。ちなみに、イテレータはポインタであるので、間接参照の解決のための演算子(*)を値取り出しのために使って、先に進めるにはインクリメント演算子(++)を使えます。
範囲for文[C++11]
上記の構文は、古いC/C++プログラマから見れば当たり前の構文ですが、他言語のスキッとした構文を見たことがあれば、もう少し何とかならないの?となりますね。ということで、C++ 11において範囲for文(range-based for)が使えるようになりました。範囲for文を使うと、上記のリストは以下のように書き直すことができます。
for(auto& it: x)
{
cout << it << endl;
}
やりました、すごくスッキリしましたね。しかも、間接参照演算子もなくなって、イテレータがポインタであることを意識しないで済むようになりました。その替わりといっては何ですが、for文のカッコの中の書き方には、Modern C++ならではの使い分けが必要です。こんな感じになっています。
- auto& it:イテレータ自身を変更でき、コンテナ要素も変更できる
- auto it:イテレータ自身は変更できるが、コンテナ要素は変更できない
- const auto& it:イテレータ自身もコンテナ要素も変更できない
要は、参照の性質そのものです。特に、2番目の書式ではコンテナ要素の内容がコピーされるので、コピーコンストラクタが必要なことに注意してください。なお、範囲for文は、コンパイル時に従来のfor文に展開されます。そのため、コンテナの実装にはbegin()とend()が必要なことは変わらないのです。
- 修正履歴
-
- 2024/05/28 10:56 当初は次回が最終回の想定でしたが、その前にもう1回分の記事を公開したく「まとめ」を更新しました。
この記事は参考になりましたか?
- Modern C++入門連載記事一覧
-
- コンテナに正規表現も! Modern C++に必須の基本ライブラリ
- Modern C++のモダンな言語仕様はもっとある! C++ 14から最新版までまとめて紹...
- 今どきのプログラミング言語では当たり前! Modern C++で使えるモダンな言語仕様
- この記事の著者
-
WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)
<WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書、記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
-
山田 祥寛(ヤマダ ヨシヒロ)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数。
※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です
この記事は参考になりましたか?
この記事をシェア
