SHOEISHA iD

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

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

C++の新機能を理解する――consteval if文と明示的オブジェクトパラメータthisなど言語仕様の強化

【C++の新機能を理解する】最新バージョンを徹底解説! 第1回

コンパイル時に条件判定できるconsteval if文

 C++ 23では、constexpr文脈として呼ばれているかを判断するconsteval if文により、constexpr関数から即時関数を呼び出せるようになりました。

 C++ 23で導入されたconsteval if文を使うと、constexpr文脈におけるconsteval関数の呼び出しが可能になります。まずは、consteval if文の前提となるconstexprとconstevalについて、簡単におさらいしておきます。

constexprとconsteval

 C++ 11で導入されたconstexprは、定数式を表現するための機能です。

 constexprを利用すると、コンパイル時に決まる定数、コンパイル時に実行できる関数、コンパイル時にリテラルとして振る舞うクラスを定義できます。いずれもコンパイル時にというのがポイントで、実行フェーズでは最終的に評価済みのものを使うので高速化できます。

 constexprが指定された関数は、コンパイル時と実行時の双方で評価されます。通常の関数としても、コンパイル時にのみ呼び出される関数としても使うことができるのです。

 以下のリストは、べき乗を計算するpow関数をconstexpr関数として定義しています(1)。(2)ではconstexpr文脈でpowが呼び出されるので、pow_constはリテラルとしての125となります。

 これに対して(3)では非constexpr文脈であり、通常の関数呼び出しとしてpow_dynamicは変数値としての125となります。

リスト constexpr.cpp
constexpr int pow(int base, int exp) {	// (1)constexpr関数の定義
    int result {1};
    for (int i = 0; i < exp; ++i) {
        result *= base;
    }
    return result;
}
…略…
constexpr int pow_const = pow(5, 3);	// (2)constexpr文脈で呼び出す
cout << pow_const << endl;	// 125
int num = 5;
int pow_dynamic = pow(num, 3);	// (3)非constexpr文脈で呼び出す
cout << pow_dynamic << endl;	// 125

 これに加えて、C++ 20ではconstevalが導入されました。constevalを指定された関数はコンパイル時にのみ評価される関数となります。これを「即時関数」と言います。

 即時関数の評価結果はconstexpr文脈で受け取る必要があり、即時関数を呼び出せるのは即時関数のみです。

 以下のリストは、pow関数を即時関数として定義したので(1)(2)は問題ないものの、(3)ではコンパイルエラーになります。

リスト consteval.cpp
consteval int pow(int base, int exp) {	// (1)即時関数の定義
…略…
constexpr int pow_const = pow(5, 3);	// (2)constexpr文脈で呼び出せる
int num = 5;
int pow_dynamic = pow(num, 3);	// (3)非constexpr文脈ではコンパイルエラー

 comstexpr関数の中でconsteval関数を呼び出すなど、組み合わせて使う際に不都合なところがあるため、定数式として評価されているかを判定できるconsteval if文が利用可能になりました。

consteval if文

 consteval if文を使うと、以下のリストのようにコンパイル時と実行時のコードを分けることができます。

リスト consteval_if.cpp
constexpr int fractional(int a) {
  if consteval {	// (1)コンパイル時か実行時か仕分ける
    return (a == 0)? 1 : fractional(a - 1) * a;	// (2)コンパイル時のコード
  } else {
    int r = 1;	// (3)実行時のコード
    while (a > 0) {
      r *= a;
      --a;
    }
    return r;
  }
}
…略…
constexpr int static_fractional = fractional(5);	// (4)constexpr文脈
cout << static_fractional << endl;	// 120

int dynamic_fractional = fractional(5);	// (5)非constexpr文脈
cout << dynamic_fractional << endl;	// 120

 fractional関数は階乗を求める関数です。constexprの指定で、コンパイル時でも実行時でも評価される関数になっています。

 (1)のif consteval(consteval if文)により、コンパイル時には(2)のブロックが、実行時には(3)のブロックが、それぞれ評価されます。これにより、(4)ではconstexpr文脈(2)の評価結果、(5)では非constexpr文脈(3)の評価結果が、それぞれ得られることになります。

明示的なオブジェクトパラメータthis

 C++ 23では、オブジェクトパラメータthisを、メンバ関数の引数に明示的に宣言できるようになりました。

 C++ 23において、オブジェクトパラメータthisを明示的にメンバ関数の引数に指定できるようになりました。オブジェクトがconst/非constなのか、左辺値(&)なのか右辺値(&&)なのかを明示できるようになり、どのような文脈で呼ばれる関数なのかが分かりやすくなります。

[NOTE]左辺値と右辺値

 左辺値(lvalue)は「特定の記憶場所を示す式」で、変数がその代表です。右辺値(rvalue)は「記憶位置を持たない値」で、関数の戻り値や計算式の評価結果などが相当します。代入演算子の左辺か右辺かには関係なく、あくまでも値の性質であるということを理解しましょう。なおC++ 11以降は、参照型「T&」は左辺値参照と呼び、「T&&」は右辺値参照と呼びます。

従来のthis――暗黙のthis

 クラスや構造体のメンバ関数には、暗黙の引数としてthisが自動的に渡されるようになっており、thisを通じてオブジェクトのメンバへアクセスすることが可能です。これは「暗黙のthis」と呼ばれます。

 以下のリストの関数は、引数にはthisはありませんが、処理内容にはthisが使われています。

リスト implicit_this.cpp
struct person {
  string name;
  int age;

  person(string n, int a) : name(n), age(a) {}

  string get_name() const {	// (1)
    return this->name;	// (2)
  }

  void set_name(string name) {
    this->name = name;
  }
}
…略…
person p("Nao Yamauchi", 30);
cout << p.get_name() << endl;	// Nao Yamauchi
cout << p.get_age() << endl;	// 30
p.set_name("Shino Onodera");
p.set_age(25);
cout << p.get_name() << endl;	// Shino Onodera
cout << p.get_age() << endl;	// 25

 thisはポインタであり、(2)のようにアロー演算子(->)を使ってメンバにアクセスします。また、関数がオブジェクトを変更するかどうかは、(1)のように関数宣言自体にconst修飾子を付与します。

明示的なthis

 オブジェクトパラメータthisを明示的にメンバ関数の引数に指定すると、以下のリストのようにオブジェクトのconst/非const、lvalue/rvalueを明示できます。

リスト explicit_this.cpp
auto get_name(this const person& self) {	// (1)明示的なthis(const)
  return self.name;	// (2)「self.」でアクセスする
}

void set_name(this person& self, string name) {	// (3)明示的なthis(非const)
  self.name = name;
}

 明示的オブジェクトパラメータは、(1)のようにthisキーワードを付けて、あとは通常の引数と同様に宣言します。引数の名前は、他言語でも用いられるselfを使うことが多いようです。selfを使ったメンバーへのアクセスは、(2)のようにドット演算子(.)を使います。

 (1)と(3)では、selfの型が異なります。(1)は参照のみで不変であるのでconst修飾されており、(3)は更新するためconst修飾子は付きません。このように、どのようにオブジェクトを操作するのかということが明確になり、オーバーロードも可能になるなどのメリットが生まれます。

 なお、この機能の元となった提案書P0847R7では、機能の名称が「Deducing this」(推論するthis)となっていますが、意味的には「Explicit this」(明示的なthis)の方が通りがよさそうです。

まとめ

 今回は、C++ 23における言語仕様面の強化/変更点を、consteval if文と明示的オブジェクトパラメータthisを中心に紹介しました。

 次回は、今回に引き続き言語仕様面の強化/変更点を、静的なoperator()/operator[]、多次元対応operator[]を中心に紹介します。

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

この記事の著者

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

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

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

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

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/22547 2025/11/19 11:00

おすすめ

アクセスランキング

アクセスランキング

イベント

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

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

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

メールバックナンバー

アクセスランキング

アクセスランキング