導入
計算のなかには、プログラムを動かす前に、コンパイルの時点で結果を確定させておけるものがあります。それを可能にするのが 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;