SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Modern C++入門

今どきのプログラミング言語では当たり前! Modern C++で使えるモダンな言語仕様

第6回 スコープ付き列挙型やイテレータはとても便利


スコープ付き列挙型[C++11]

 列挙型(enum)はC言語ですでに使えるようになっていた言語仕様で、マジックナンバー(直にコーディングされた数値など)を嫌うプログラマが好んで使っていたように思えます(筆者です)。なんてことのない定数値でも、わざわざ名前を割り当てて使うという感じです。「enum Boolean {False = 0, True}」みたいな感じですね。

従来からの列挙型

 とはいえ、マジックナンバーはコードの可読性を下げますし、値と意味の重複が起きる可能性があるので、列挙型で置き換えるのは妥当ですね。例えば、以下のリストです。

リスト scoped_enum.cpp
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は同じ働きをします)。

リスト scoped_enum.cpp
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」です。

リスト scoped_enum.cpp
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())を指すイテレータを取得し、範囲内で要素にアクセスするといった使い方が基本です。これを使って繰り返しをするのが以下のリストです。

リスト range_for.cpp
#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文を使うと、上記のリストは以下のように書き直すことができます。

リスト range_for.cpp
for(auto& it: x)
{
    cout << it << endl;
}

 やりました、すごくスッキリしましたね。しかも、間接参照演算子もなくなって、イテレータがポインタであることを意識しないで済むようになりました。その替わりといっては何ですが、for文のカッコの中の書き方には、Modern C++ならではの使い分けが必要です。こんな感じになっています。

  • auto& it:イテレータ自身を変更でき、コンテナ要素も変更できる
  • auto it:イテレータ自身は変更できるが、コンテナ要素は変更できない
  • const auto& it:イテレータ自身もコンテナ要素も変更できない

 要は、参照の性質そのものです。特に、2番目の書式ではコンテナ要素の内容がコピーされるので、コピーコンストラクタが必要なことに注意してください。なお、範囲for文は、コンパイル時に従来のfor文に展開されます。そのため、コンテナの実装にはbegin()とend()が必要なことは変わらないのです。

次のページ
usingと型エイリアス[C++11]

修正履歴

この記事は参考になりましたか?

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編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/19478 2024/05/28 10:56

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー