Menu
Coddy logo textTech

constexprとconsteval

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 87/104。

C++では、実行時ではなくコンパイル時に計算を行うことが可能であり、これによりパフォーマンスを大幅に向上させることができます。constexprキーワード(C++11)およびconstevalキーワード(C++20)により、式がいつ評価されるかを制御できるようになります。

constexpr 関数は、定数の引数が与えられた場合にはコンパイル時に評価されることができますが、定数ではない入力を用いて実行時に実行することも可能です:

#include <iostream>

constexpr int square(int n) {
    return n * n;
}

int main() {
    constexpr int compileTime = square(5);  // コンパイル時に評価される
    
    int x = 7;
    int runtime = square(x);  // 実行時に評価される
    
    std::cout << compileTime << "\n";  // 25
    std::cout << runtime << "\n";      // 49
}

コンパイル時評価を保証する必要がある場合は、consteval を使用します。consteval 関数は定数を生成しなければなりません。実行時の値を使用して呼び出すと、コンパイルエラーになります。

consteval int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

int main() {
    constexpr int result = factorial(5);  // OK: 120 はコンパイル時に計算されます
    
    // int x = 5;
    // int bad = factorial(x);  // エラー: x は定数ではありません
}

constexpr は変数やクラスのコンストラクタにも使用でき、オブジェクト全体をコンパイル時に生成することが可能になります。これは、ルックアップテーブルや設定値、あるいはプログラムの実行中に変更されないあらゆるデータに対して特に有用です。

challenge icon

チャレンジ

簡単

コードを3つのファイルに分けて構成します:

  • MathUtils.h: コンパイル時数学関数を定義します。

    以下の関数を作成してください:

    • cubeint を受け取り、その立方体 (n * n * n) を返す constexpr 関数
    • triangularNumber — 公式 n * (n + 1) / 2 を使用して n 番目の三角数を計算する constexpr 関数。これは常にコンパイル時に評価される必要があります。
    • sumOfSquares — 2つの整数を受け取り、それらの二乗の和 (a*a + b*b) を返す constexpr 関数
  • Config.h: コンパイル時定数を使用して設定構造体を作成します。

    3つの整数 widthheightdepth を受け取る constexpr コンストラクタを持つ Config 構造体を定義します。これらを公開メンバとして保存してください。また、width * height * depth を返す volume() という constexpr メソッドを追加します。

    構造体の下に、値 10、20、5 で初期化された DEFAULT_CONFIG という名前の constexpr グローバル定数を作成します。

  • main.cpp: 実行時の値を表す2つの整数を入力から読み取ります。

    まず、constexpr 変数を作成することでコンパイル時評価を実演します:

    • cube(4) を constexpr 変数に格納し、次のように出力します:Cube of 4: [value]
    • triangularNumber(10) を constexpr 変数に格納し、次のように出力します:10th triangular number: [value]
    • デフォルト設定のボリュームを出力します:Default volume: [value]

    次に、2つの入力値を使用して、constexpr 関数が実行時にも機能することを実演します:

    • 最初の入力で cube() を呼び出し、次のように出力します:Cube of [input]: [result]
    • 両方の入力で sumOfSquares() を呼び出し、次のように出力します:Sum of squares: [result]

例えば、入力が 34 の場合:

Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 3: 27
Sum of squares: 25

入力が 512 の場合:

Cube of 4: 64
10th triangular number: 55
Default volume: 1000
Cube of 5: 125
Sum of squares: 169

チートシート

C++では、constexpr (C++11) および consteval (C++20) キーワードを使用して、コンパイル時に計算を行うことができます。

constexpr 関数は、定数の引数を使用するとコンパイル時に評価されることが可能ですが、定数ではない入力を使用すると実行時にも動作します。

constexpr int square(int n) {
    return n * n;
}

constexpr int compileTime = square(5);  // コンパイル時に評価される

int x = 7;
int runtime = square(x);  // 実行時に評価される

consteval 関数は、必ずコンパイル時に評価されなければなりません。実行時の値を使用して呼び出すと、コンパイルエラーが発生します。

consteval int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

constexpr int result = factorial(5);  // OK: コンパイル時に計算される

// int x = 5;
// int bad = factorial(x);  // エラー: x は定数ではありません

constexpr は、変数、クラスのコンストラクタ、およびメソッドに使用して、コンパイル時のオブジェクト生成を可能にすることができます。

struct Config {
    int width, height, depth;
    
    constexpr Config(int w, int h, int d) 
        : width(w), height(h), depth(d) {}
    
    constexpr int volume() const {
        return width * height * depth;
    }
};

constexpr Config config(10, 20, 5);  // コンパイル時に生成される

自分で試してみよう

#include <iostream>
#include "MathUtils.h"
#include "Config.h"

using namespace std;

int main() {
    // 入力から2つの整数を読み取る
    int input1, input2;
    cin >> input1;
    cin >> input2;
    
    // TODO: コンパイル時評価を実演する
    // cube(4)を格納するconstexpr変数を作成し、"Cube of 4: [value]"と出力する
    
    // TODO: triangularNumber(10)を格納するconstexpr変数を作成する
    // そして、"10th triangular number: [value]"と出力する
    
    // TODO: デフォルト設定のボリュームを出力する: "Default volume: [value]"
    
    // TODO: constexpr関数の実行時における使用を実演する
    // input1を引数にしてcube()を呼び出し、"Cube of [input1]: [result]"と出力する
    
    // TODO: 両方の入力を引数にしてsumOfSquares()を呼び出す
    // そして、"Sum of squares: [result]"と出力する
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン