はじめに
C++のconstexprはコンパイル時計算を可能にする強力な機能。
「実行時に計算するんじゃなくて、コンパイル時に全部終わらせる」という発想、かなりロマンがある。
C++11からC++20までの進化を追いながら、constexprの全機能を解説するよ。
1. 基本的なconstexpr関数
constexpr int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
constexpr int fibonacci(int n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// コンパイル時に計算される
constexpr int fact5 = factorial(5); // 120
constexpr int fib10 = fibonacci(10); // 55
// static_assert で検証
static_assert(factorial(5) == 120, "factorial test");
2. constexpr変数
constexpr double PI = 3.14159265358979323846;
constexpr double E = 2.71828182845904523536;
constexpr int MAX_SIZE = 1024;
// constexprはconstより厳密
// const int x = get_runtime_value(); // OK
// constexpr int y = get_runtime_value(); // エラー!
3. constexprクラス
class Point {
public:
int x, y;
constexpr Point(int x = 0, int y = 0) : x(x), y(y) {}
constexpr Point operator+(const Point& other) const {
return Point(x + other.x, y + other.y);
}
constexpr int dot(const Point& other) const {
return x * other.x + y * other.y;
}
constexpr int manhattanDistance() const {
return (x >= 0 ? x : -x) + (y >= 0 ? y : -y);
}
};
constexpr Point p1(3, 4);
constexpr Point p2(1, 2);
constexpr Point p3 = p1 + p2; // (4, 6)
constexpr int dot = p1.dot(p2); // 11
4. constexpr配列操作
constexpr int sum_array(const int* arr, size_t n) {
int result = 0;
for (size_t i = 0; i < n; ++i) {
result += arr[i];
}
return result;
}
constexpr int arr[] = {1, 2, 3, 4, 5};
constexpr int sum = sum_array(arr, 5); // 15
static_assert(sum == 15, "sum test");
5. constexpr文字列操作
constexpr size_t str_length(const char* str) {
size_t len = 0;
while (str[len] != '\0') ++len;
return len;
}
constexpr bool str_equal(const char* a, const char* b) {
while (*a && *b && *a == *b) {
++a; ++b;
}
return *a == *b;
}
constexpr size_t len = str_length("Hello"); // 5
static_assert(str_equal("test", "test"), "equal test");
6. constexpr if (C++17)
template<typename T>
constexpr auto process(T value) {
if constexpr (is_integral_v<T>) {
return value * 2; // 整数は2倍
} else if constexpr (is_floating_point_v<T>) {
return value / 2; // 浮動小数点は半分
} else {
return value; // その他はそのまま
}
}
constexpr auto int_result = process(10); // 20
constexpr auto float_result = process(10.0); // 5.0
7. constexpr lambda (C++17)
constexpr auto square = [](int x) { return x * x; };
constexpr auto cube = [](int x) { return x * x * x; };
constexpr int sq = square(5); // 25
constexpr int cb = cube(3); // 27
static_assert(square(5) == 25, "square test");
8. コンパイル時アルゴリズム
constexpr bool is_prime(int n) {
if (n < 2) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) return false;
}
return true;
}
constexpr int gcd(int a, int b) {
while (b != 0) {
int t = b;
b = a % b;
a = t;
}
return a;
}
constexpr int lcm(int a, int b) {
return a / gcd(a, b) * b;
}
static_assert(is_prime(17), "17 is prime");
static_assert(gcd(24, 36) == 12, "gcd test");
9. コンパイル時ルックアップテーブル
template<size_t N>
constexpr array<int, N> make_square_table() {
array<int, N> result{};
for (size_t i = 0; i < N; ++i) {
result[i] = static_cast<int>(i * i);
}
return result;
}
template<size_t N>
constexpr array<bool, N> make_prime_sieve() {
array<bool, N> sieve{};
for (size_t i = 2; i < N; ++i) {
sieve[i] = true;
}
for (size_t i = 2; i * i < N; ++i) {
if (sieve[i]) {
for (size_t j = i * i; j < N; j += i) {
sieve[j] = false;
}
}
}
return sieve;
}
// コンパイル時に生成
constexpr auto squares = make_square_table<10>();
constexpr auto primes = make_prime_sieve<30>();
10. constexpr状態機械
enum class State { Start, Running, Paused, Stopped };
constexpr State next_state(State current, bool input) {
switch (current) {
case State::Start: return input ? State::Running : State::Start;
case State::Running: return input ? State::Paused : State::Stopped;
case State::Paused: return input ? State::Running : State::Stopped;
case State::Stopped: return State::Stopped;
}
return State::Stopped;
}
11. コンパイル時バイナリサーチ
constexpr int binary_search(const int* arr, int n, int target) {
int left = 0, right = n - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) return mid;
if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
}
constexpr int arr[] = {1, 3, 5, 7, 9, 11, 13};
constexpr int idx = binary_search(arr, 7, 7); // 3
実行結果
=== 基本constexpr関数 ===
factorial(5) = 120
fibonacci(10) = 55
=== constexprクラス ===
Point(3,4) + Point(1,2) = (4,6)
Dot product: 11
Manhattan distance: 7
=== constexpr配列操作 ===
Sum: 15
Max: 5
=== constexpr if ===
process(10) = 20
process(10.0) = 5
=== constexpr lambda ===
square(5) = 25
cube(3) = 27
=== constexprアルゴリズム ===
is_prime(7) = 1
gcd(48, 18) = 6
lcm(4, 6) = 12
=== constexprルックアップテーブル ===
Square table: 0 1 4 9 16 25 36 49 64 81
Primes < 30: 2 3 5 7 11 13 17 19 23 29
constexprの進化
| バージョン | 追加機能 |
|---|---|
| C++11 | 基本的なconstexpr関数(1文return) |
| C++14 | ループ、ローカル変数、複数文 |
| C++17 | constexpr if、constexpr lambda |
| C++20 | constexpr std::vector、virtual関数 |
まとめ
constexprを活用することで:
- ランタイムコストをゼロに
- コンパイル時エラー検出
- 最適化の促進
モダンC++では積極的にconstexprを使いましょう!