SHOEISHA iD

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

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

Modern C++入門

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

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


  • X ポスト
  • このエントリーをはてなブックマークに追加

 本連載では、Modern C++と称されるC++について、Modern C++らしい言語仕様をピックアップし紹介していきます。第6回は、プログラミング言語のスマートさを演出するいろんな言語仕様を紹介します。今どきのプログラミング言語では当たり前になっているような、スコープ付き列挙型、イテレータ(範囲付きfor)も使えるようになっています。これらをはじめとした、モダンで便利な言語仕様をピックアップして理解します。

  • X ポスト
  • このエントリーをはてなブックマークに追加

はじめに

 C言語から派生したオブジェクト指向プログラミング言語であるC++は、21世紀に入ってまったく別物とも言えるプログラミング言語に成長していきました。それは、Modern C++と称されています。1990年代にC++を触っていたプログラマが現在の仕様を知れば、隔世感に苛まれるのではないでしょうか。本連載では、かつてはC++をたしなんでいたという方、今からC++言語を始めるという方に向けて、Modern C++らしい言語仕様をピックアップし紹介していくことで、今のC++言語の姿を理解していただきます。

対象読者

  • かつてはC++をたしなんでいたという方
  • 今からC++言語を始めるという方
  • モダンなプログラミング言語のパラダイムに興味のある方

必要な環境

 本記事のサンプルコードは、以下の環境で動作を確認しています。

  • macOS Sonoma/Windows 11
    • Xcode Command Line Tools 2395
    • MinGW GCC 9.2.0

constexprとnullptr

 20世紀のC++プログラマ(筆者)が、今(21世紀)のC++を知ってあっと驚く連載の第6回です。今回は、最近のプログラミング言語では当たり前の、モダンな言語仕様をピックアップして紹介します。まずは、シンプルな話としてconstexprとnullptrあたりから始めましょう。

[NOTE]C++プログラムのコンパイル

 以降のサンプルは、基本的に「gcc -o 出力先ファイル名 ソースファイル名」でコンパイルできますが、環境によってはライブラリやC++バージョンの指定が必要になることがあります。コンパイルやリンクでエラーが発生するときには、以下のようにライブラリの指定(-lstdc++)、C++バージョンの指定(-std=c++11)の追加を検討してください。

% gcc -o sample sample.cpp -lstdc++ -std=c++11	// c++14, c++17, c++20など

定数定義はconstexprで[C++11]

 C/C++言語にはconst修飾子というものがありまして、変数宣言に適用した場合にはその変数を不変(immutable)とすることができます。例えば、以下のような感じです(マクロを定数代わりに使うということもよく行われていましたが、全く安全でないのでここでは割愛します)。

リスト constexpr_nullptr.cpp
const int a = 100;
a = 200;		// コンパイルエラー
// cannot assign to variable 'a' with const-qualified type 'const int'
const int b = rand();
b = 300;      // コンパイルエラー
// cannot assign to variable 'b' with const-qualified type 'const int'

 const修飾されたa(あるいはb)には再代入できない、というエラーになります。aとbで異なるのは、aがリテラルによる初期化、bが関数による初期化ということです。const修飾子では、両者を特に区別せず、とにかく再代入不可とします。

 C++ 11では、前者のようなケースにはconstexpr指定子を使って明確に区別することになりました。

リスト constexpr_nullptr.cpp
constexpr int c = 200;
c = 400;		// コンパイルエラー
constexpr int d = rand();   // コンパイルエラー
// constexpr variable 'd' must be initialized by a constant expression

 前者は、const修飾子の例と同じで再代入時にエラーとなりますが、後者は宣言時に定数での初期化が必要というエラーとなります。つまり、const修飾子とconstexpr指定子には以下の違いがあるということです。

  • const:初期化子が定数か変数かは問わない。とにかく変更できない変数とする
  • constexpr:初期化子は定数でなければならない

 constexpr指定子によって不変とされた変数は、コンパイル時に値が決まるので、コンパイル後のバイナリに直値として埋め込まれます(つまり実行効率の向上になります)。const修飾子によって不変とされた変数は、初期化子や最適化によってケースバイケース(メモリに置かれたり、直値となったり)となります。よって、明らかな定数にはconstexpr指定子を使うのが、C++ 11以降では正しい使い分けと言えます。

 なお、constexpr関数というものもあり、これは入力も戻り値もconstexprとなる関数です。この関数による初期化はOKです。ただしあくまでもコンパイラが追跡できて、最終的な結果が定数であると判断できた場合のみです。以下のリストでは、正しいconstexpr関数と、呼び出せないconstexpr関数の例を示しています。

リスト constexpr_nullptr.cpp
// 入力がconstexprで計算結果もconstexprとできるので呼び出し可能
constexpr bool is_greater(int a, int b) {
    a += 3;
    return (a > b)? true : false;
}

// 入力がconstexprでも関数内でconstexprでない関数呼び出しがあるので呼び出し不可
constexpr bool is_lower(int a, int b) {
    a += rand();				// コンパイルエラー
    return (a < b)? true : false;
}

constexpr bool greater = is_greater(5, 4); 	// OK
constexpr bool lower = is_lower(8, 2); 		// 関数は呼び出し不可

NULLポインタはnullptrで[C++11]

 C/C++言語では、長い間NULLポインタの明確な定義がありませんでした。特にC言語では、プログラマによって以下のように勝手に定義して使っていたのではないかと思います。

リスト constexpr_nullptr.cpp
#define NULL ((void *)0)	// あるいは単に「0」とするとか…

void *ptr = NULL;

 NULLポインタを便宜上アドレス0番地へのポインタとするわけです。このため、NULLポインタでメモリに書き込んでしまったことをプログラム終了時に検出し、Null pointer assignmentといったエラーを吐き出す処理系もありました。C++ 03では少し改善されて、NULLマクロが定義済みとなりました。なので、上のリストのように#defineしてコンパイルすると再定義エラーになります。ちなみに、このNULLマクロは、gccでは__nullという値で定義されています。__nullには移植性はないようなので、そのまま使うのは避けた方がいいでしょう。

 ただ、いつまでもこのようなマクロを使うのはどうかということで、C++ 11において言語仕様としてnullptrが定められました。nullptrは当たり前ですが整数型などに変換されないので、安全に使うことができます。かつて、\0をヌル文字と表現しているせいもあって、ポインタのNULLとヌル文字のNULLがごっちゃになっているケースがありました。NULLが0に定義されていると、どちらにでも適用できてしまうので、いらぬバグの元でした。

 nullptrを使うと、ポインタ変数を明示的にNULLポインタとして初期化できます。また、連載第3回で紹介したスマートポインタも、コンストラクタによって暗黙的にnullptrに初期化されます。

リスト constexpr_nullptr.cpp
int *nptr = nullptr;
unique_ptr<int> int_ptr;
shared_ptr<int> str_ptr;
*nptr = 1;			// 実行時segmentation fault 

 なお、このリストの最終行はnullptrによる書き込みとなりますが、コンパイル時には指摘してくれずに実行時にエラーとなります。このように、コンパイル時にnullptrを追跡してくれるわけではなさそうです。

会員登録無料すると、続きをお読みいただけます

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

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

メールバックナンバー

次のページ
スコープ付き列挙型[C++11]

修正履歴

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

  • X ポスト
  • このエントリーをはてなブックマークに追加
Modern C++入門連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/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本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

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

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

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/19478 2024/05/28 10:56

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング