本文へスキップ
BecomeCoder

C++コース · 第18章 実務のC++ ― 分離とモダンな型 · レッスン73

constexpr と範囲for・auto返り値 ― コンパイル時計算

ブラウザで完結

導入

計算のなかには、プログラムを動かす前に、コンパイルの時点で結果を確定させておけるものがあります。それを可能にするのが constexpr です。実行時の負担をゼロにできる、実務でもよく使われる指定です。

説明

constexpr関数に付けると、「コンパイル時にも、実行時にも呼び出せる関数」になります。引数がコンパイル時に決まっていれば、コンパイラが計算まで終わらせてしまいます。

#include <iostream>
#include <vector>
using namespace std;

constexpr int factorial(int n) {         // コンパイル時にも実行時にも使える
    return (n <= 1) ? 1 : n * factorial(n - 1);
}

int main() {
    constexpr int f5 = factorial(5);     // コンパイル時に確定 → 実行コストなし
    cout << f5 << endl;                  // 120

    vector<int> v = {1, 2, 3, 4, 5};
    int total = 0;
    for (const auto& x : v) {            // 読み取り専用の参照で回す(コピーしない)
        total += x;
    }
    cout << total << endl;               // 15
    return 0;
}

C++20 では、さらに強い consteval も加わりました。constexpr は「コンパイル時にも実行時にも呼べる」のに対し、consteval は「必ずコンパイル時にしか呼べない」関数を作れます(consteval int square(int n) { return n * n; } のように書きます)。ここでは概念の紹介にとどめ、実行の確認は constexpr の側で行います。

戻り値の型に auto を使うと、return する式から型を自動推論してくれます。長いテンプレートの戻り値型を書かずに済み、実務でもよく使われます。

auto add(int a, int b) {   // 戻り値の型を書かなくてよい
    return a + b;          // a + b は int なので、戻り値も int と推論される
}

範囲for も auto と組み合わせて使い分けます。中身を書き換えたいなら auto&(参照)、読むだけなら const auto&(コピーしない読み取り専用の参照)が定石です。

#include <iostream>
#include <vector>
using namespace std;

auto sumAll(const vector<int>& v) {    // auto 返り値
    int total = 0;
    for (const auto& x : v) {          // 読み取り専用
        total += x;
    }
    return total;
}

int main() {
    vector<int> v = {1, 2, 3, 4, 5};

    for (auto& x : v) {    // 書き換え用の参照
        x *= 2;
    }

    cout << sumAll(v) << endl;   // 30
    return 0;
}

やってみよう

右のコードを実行し、factorial の引数を変えたり、v の中身を変えたりして結果を確かめましょう。for (auto& x : v)for (const auto& x : v) に変えると、書き換えができなくなることも試してみてください。

演習

constexpr 関数 cube(int n)n * n * n を返す)を定義し、constexpr int c = cube(3); として結果を表示してください(結果は 27)。

ヒント1を見る

constexpr int cube(int n) { return n * n * n; } を main の外に定義します。

ヒント2を見る

constexpr int c = cube(3); cout << c << endl;

実際に動かしてみよう

下のエディタにC++を書いて「コンパイル & 実行」を押すと、ブラウザ内で本物のC++コンパイラ(clang + libc++)がその場でコンパイルして実行し、出力が表示されます。std::string・vector・クラス・テンプレート・STL などモダンC++がそのまま動きます。本文の例を書き換えて試しながら進めましょう(初回だけコンパイラの読み込みに時間がかかります。以降はキャッシュされます。この回は標準入力(cin)は使わず、値はコードに直接書きます)。

C/C++ — ブラウザ内で本物のコンパイラで実行

ブラウザ内で本物のC/C++コンパイラ(clang + libc++ / WebAssembly)を動かします。構造体・malloc/free・std::string・vector・クラス・テンプレート・STL などがそのまま実行できます(初回のみコンパイラの読み込みに時間がかかります。以降はブラウザにキャッシュされます)。
スクロールして表示された時点でも自動で読み込まれます。