SHOEISHA iD

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

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

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

Modern C++入門

Modern C++のモダンな言語仕様はもっとある! C++ 14から最新版までまとめて紹介

第7回 コンセプトとモジュールで安全・便利なプログラミング

初期化式付きの制御構文

 20世紀のC++プログラマ(筆者)が、今(21世紀)のC++を知ってあっと驚く連載の第7回です。今回は、前回に引き続きモダンな言語仕様を、C++ 14以降にフォーカスして紹介します。まずは、初期化式を持てるようになったif文とswitch文、そしてfor文です。

初期化式付きif文[C++17]

 初期化式を持てるという意味は何でしょうか? これまでのif文では、何らかの変数を含む式を条件式とする場合、その変数はif文の外部で宣言されている必要がありました。例えば、以下のリストのようにです。

srand((unsigned)time(NULL));
auto x = rand();
if (x % 2 == 0) …

 仮に変数xがif文終了後には用済みとなっても、変数xは引き続き有効になっています。もちろん、xは再宣言できませんが、別の用途で使うことができます。ただ、変数の用途が途中で変わるのはトラブルの元であり、あまり好ましいこととは言えません。

 そこで、C++ 17においてはif文に初期化式を指定できるようになりました。for文のように、条件式の前に初期化式が来るという感じです。if文の内部でのみ有効としたい変数は、初期化式で宣言すればif文の終了とともに消滅します。以下のリストでは、変数xを乱数で初期化し、すぐに偶数か奇数か判定しています。if文が終了すれば、変数xは別の型で再宣言できます。

リスト:init_expression.cpp
if(auto x = rand(); x % 2 == 0) {
    cout << x << "は偶数です。" << endl;
} else {
    cout << x << "は奇数です。" << endl;
}
// 実行結果:97872224は偶数です。
auto x = 1.0;	// 別の型で再宣言可能

初期化式付きswitch文[C++17]

 switch文も同様です。以下のリストでは、変数yを乱数で初期化し、2の剰余算で偶数、奇数を振り分けています。この場合も、switch文が終了すれば、変数yは別の型で再宣言できます。

リスト:init_expression.cpp
switch(auto y = rand(); y % 2) {
    case 0:
        cout << y << "は偶数です。" << endl;
        break;
    case 1:
        cout << y << "は奇数です。" << endl;
        break;
}
// 実行結果:2113478813は奇数です。
auto y = 'y';	// 別の型で再宣言可能

初期化式付きfor文[C++20]

 最後のfor文も同様です。「for文には最初から初期化式があるでしょ?」というツッコミが来そうですが、範囲for文すなわちコレクションに適用する方です。この範囲for文、確かに便利なのですが、例えばループ中で「今、何番目?」というのを把握することができませんでした。それをやりたい場合、カウンタ変数を用意し、ループ中でカウントアップさせるなどする必要がありました。ここでも、ループ中でしか必要ない変数がfor文終了後も残ってしまうという問題が起きます。以下のリストでは、配列の中身を走査して表示しますが、カウンタ変数countを用意して順番も表示しています。

リスト:init_expression.cpp

auto ary = {"Mon", "Tue", "Wed", "Thu", "Fri"};
for(int count = 1; auto& a: ary) {
    cout << count++ << ":" << a << endl;
}
// 1:Mon …
auto count = "OK";	// countを宣言可能

[NOTE]C++ 23では型指定にusingによるエイリアスも指定できる

 初期化式中の型名には、typedefによる別名も指定できます。C++ 20まではusingによる別名は指定できませんでしたが、C++ 23ではこれが可能になりました。細かな改良ですが、使いやすさはエディションごとに進化しています。

タプルとバリアント

 言語仕様というよりはライブラリの拡張ですが、タプルとバリアントを紹介します。構造体によらず複数の型をまとめて扱ったり、型が特定されない値を扱うのに便利です。多くのプログラミング言語で実装されている機能ですね。

タプルstd::tuple[C++11]

 タプルは、複数の型の値の組を扱えるクラスです。配列では同じ型の値しか取り扱えませんが、タプルでは異なる型の値をまとめて関数の引数としたり、戻り値とすることができます。タプルは0個からの要素数に対応しますが、0個1個では意味がないですし、2個ではstd::pairがあるので、一般的には3個以上の場合に使います。

リスト:tuple_variant.cpp

tuple<int, double, string> tuple1 { 1, 123.45, "Hello!" };
tuple<string, int, char> tuple2 { "World!", 1, 'X' };
vector<tuple<string, double, double>> vec = {
    { "Taro", 175.5, 70.0 },
    { "Hanako", 160.5, 50.0 },
};

 タプルの各要素へのアクセスは、std::get関数を使います。

リスト:tuple_variant.cpp

cout << get<0>(tuple1) << "," << get<1>(tuple1) << "," << get<2>(tuple1) << endl; 
// 実行結果:1,123.45,Hello!

バリアントstd::variant[C++17]

 バリアントは、単一のオブジェクトで複数の型のデータを保持できるクラスです。そういう意味では共用体(union)に似ていますが、利用できる型を厳密に制限できるので、共用体より安全に利用することができます。以下のリストでは、int型、double型、string型の値を持てるバリアントvariant1を宣言しています。

リスト:tuple_variant.cpp

variant<int, double, string> variant1;
variant1 = 100;
variant1 = 3.14;
variant1 = "Hello, world!";

 バリアントの各要素へのアクセスは、タプルと同様にstd::get関数を使います。

リスト:tuple_variant.cpp

cout << get<int>(variant1) << endl;
// 実行結果:1

 バリアントに入っている型と取得する型が異なる場合、実行時に例外が発生します。

リスト:tuple_variant.cpp
variant1 = "Hello, world!";
cout << get<int>(variant1) << endl;
// libc++abi: terminating due to uncaught exception of type std::bad_variant_access: bad_variant_access

 バリアントに入っている値の型は、indexメソッドで取得できます。宣言時の型パラメータの順番に相当する0からのインデックスで返されるので、switch文などで型による振り分けが可能です。ただし、インデックスに対応した関数オブジェクトを定義した構造体を使うVisitorパターンを使えるstd::visit関数により、switch文などを使用せずにシンプルに振り分けも可能です。

リスト:tuple_variant.cpp
struct Visitor {
    void operator()(int) {cout << "Integer" << endl;}
    void operator()(double) {cout << "Double" << endl;}
    void operator()(string) {cout << "String" << endl;}
};
variant1 = "Hello, world!";
visit(Visitor{}, variant1);
// 実行結果:String

次のページ
コンセプト[C++20]

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

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/19709 2024/06/26 11:00

イベント

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

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

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

メールバックナンバー