S SmartDocs
시리즈: C++ cpp 362 줄 · 업데이트 2026-04-03

constexpr_basics.cpp

C++/Part5_進階主題/Ch22_constexpr與編譯期計算/constexpr_basics.cpp

// constexpr_basics.cpp
// 編譯指令:g++ -std=c++17 -Wall constexpr_basics.cpp -o constexpr_basics
//
// 本程式示範 constexpr 的基本用法:變數、函式、static_assert

#include <iostream>
#include <array>
#include <cmath>
#include <string_view>

// ============================================================
// 第一部分:constexpr 變數
// ============================================================

constexpr int MAX_BUFFER_SIZE = 1024;
constexpr double PI = 3.14159265358979323846;
constexpr double TAU = 2.0 * PI;
constexpr int GRID_SIZE = 8;
constexpr int TOTAL_CELLS = GRID_SIZE * GRID_SIZE;  // constexpr 可以引用其他 constexpr

void demo_constexpr_variables() {
    std::cout << "========================================\n";
    std::cout << "  constexpr 變數\n";
    std::cout << "========================================\n\n";

    // constexpr 值可以用作陣列大小
    int buffer[MAX_BUFFER_SIZE];
    int grid[GRID_SIZE][GRID_SIZE];
    (void)buffer; (void)grid;

    std::cout << "MAX_BUFFER_SIZE = " << MAX_BUFFER_SIZE << "\n";
    std::cout << "PI              = " << PI << "\n";
    std::cout << "TAU             = " << TAU << "\n";
    std::cout << "GRID_SIZE       = " << GRID_SIZE << "\n";
    std::cout << "TOTAL_CELLS     = " << TOTAL_CELLS << "\n";

    // 對比:const 不一定是編譯期
    const int runtime_const = std::cin.peek();  // 執行期才知道
    // constexpr int x = runtime_const;          // 錯誤!無法在編譯期確定
    (void)runtime_const;
    std::cout << "\nconst 可以在執行期初始化,constexpr 不行\n";
    std::cout << "\n";
}

// ============================================================
// 第二部分:constexpr 函式 — 基本範例
// ============================================================

constexpr int factorial(int n) {
    int result = 1;
    for (int i = 2; i <= n; ++i) {
        result *= i;
    }
    return result;
}

constexpr long long fibonacci(int n) {
    if (n <= 1) return n;
    long long a = 0, b = 1;
    for (int i = 2; i <= n; ++i) {
        long long temp = a + b;
        a = b;
        b = temp;
    }
    return b;
}

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 temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}

void demo_constexpr_functions() {
    std::cout << "========================================\n";
    std::cout << "  constexpr 函式\n";
    std::cout << "========================================\n\n";

    // 編譯期求值
    constexpr int fact5 = factorial(5);
    constexpr long long fib10 = fibonacci(10);
    constexpr bool prime17 = is_prime(17);
    constexpr int gcd_12_8 = gcd(12, 8);

    std::cout << "factorial(5)  = " << fact5 << "\n";
    std::cout << "fibonacci(10) = " << fib10 << "\n";
    std::cout << "is_prime(17)  = " << std::boolalpha << prime17 << "\n";
    std::cout << "gcd(12, 8)    = " << gcd_12_8 << "\n";

    // constexpr 函式也可以在執行期使用
    int n;
    std::cout << "\n(在執行期使用 constexpr 函式)\n";
    n = 7;
    std::cout << "factorial(" << n << ") = " << factorial(n) << "\n";
    std::cout << "fibonacci(" << n << ") = " << fibonacci(n) << "\n";
    std::cout << "is_prime(" << n << ")  = " << std::boolalpha << is_prime(n) << "\n";

    // 印出質數列表
    std::cout << "\n2 到 50 之間的質數:\n  ";
    for (int i = 2; i <= 50; ++i) {
        if (is_prime(i)) std::cout << i << " ";
    }
    std::cout << "\n\n";
}

// ============================================================
// 第三部分:static_assert 與 constexpr
// ============================================================

constexpr int PROTOCOL_VERSION = 3;
constexpr int MIN_VERSION = 2;
constexpr int MAX_VERSION = 5;

// 編譯期驗證配置
static_assert(PROTOCOL_VERSION >= MIN_VERSION, "協議版本過低");
static_assert(PROTOCOL_VERSION <= MAX_VERSION, "協議版本過高");
static_assert(MAX_BUFFER_SIZE >= 256, "緩衝區必須至少 256 bytes");
static_assert(MAX_BUFFER_SIZE % 8 == 0, "緩衝區大小必須是 8 的倍數");

// 驗證自定義函式的結果
static_assert(factorial(5) == 120, "factorial 計算錯誤");
static_assert(fibonacci(10) == 55, "fibonacci 計算錯誤");
static_assert(is_prime(7), "7 應該是質數");
static_assert(!is_prime(4), "4 不應該是質數");
static_assert(gcd(12, 8) == 4, "gcd 計算錯誤");

void demo_static_assert() {
    std::cout << "========================================\n";
    std::cout << "  static_assert 編譯期驗證\n";
    std::cout << "========================================\n\n";

    std::cout << "以下 static_assert 在編譯期已通過驗證:\n";
    std::cout << "  ✓ PROTOCOL_VERSION 在 [" << MIN_VERSION << ", " << MAX_VERSION << "] 範圍內\n";
    std::cout << "  ✓ MAX_BUFFER_SIZE (" << MAX_BUFFER_SIZE << ") >= 256\n";
    std::cout << "  ✓ MAX_BUFFER_SIZE 是 8 的倍數\n";
    std::cout << "  ✓ factorial(5) == 120\n";
    std::cout << "  ✓ fibonacci(10) == 55\n";
    std::cout << "  ✓ is_prime(7) == true\n";
    std::cout << "  ✓ gcd(12, 8) == 4\n";
    std::cout << "\n如果任何斷言失敗,程式無法編譯\n";

    // 型別特性的 static_assert
    static_assert(sizeof(int) >= 4, "int 至少需要 4 bytes");
    static_assert(sizeof(long long) >= 8, "long long 至少需要 8 bytes");
    std::cout << "  ✓ sizeof(int) = " << sizeof(int) << " >= 4\n";
    std::cout << "  ✓ sizeof(long long) = " << sizeof(long long) << " >= 8\n";
    std::cout << "\n";
}

// ============================================================
// 第四部分:const vs constexpr 比較
// ============================================================

// constexpr 保證在編譯期確定

int get_runtime_value() {
    return 42;
}

void demo_const_vs_constexpr() {
    std::cout << "========================================\n";
    std::cout << "  const vs constexpr 比較\n";
    std::cout << "========================================\n\n";

    const int a = 10;                     // 編譯期已知(但不強制)
    const int b = get_runtime_value();    // 執行期才知道

    constexpr int c = 10;                 // 編譯期保證
    // constexpr int d = get_runtime_value(); // 編譯錯誤!

    // const 可用於某些編譯期情境(如果編譯器能推導)
    // 但 constexpr 更明確
    int arr1[a];  // 可能 OK(編譯器特定行為)
    int arr2[c];  // 一定 OK
    (void)arr1; (void)arr2;

    std::cout << "const int a = 10;               // 可以是編譯期\n";
    std::cout << "const int b = get_runtime();     // 只能是執行期\n";
    std::cout << "constexpr int c = 10;            // 保證編譯期\n";
    std::cout << "// constexpr int d = get_runtime(); // 編譯錯誤\n\n";

    // constexpr 隱含 const
    // constexpr int x = 10;
    // x = 20;  // 錯誤:constexpr 變數隱含 const
    std::cout << "constexpr 變數隱含 const,不可修改\n";

    // const 指標 vs constexpr 指標
    static constexpr int value = 100;
    constexpr const int* ptr = &value;  // 指標本身是 constexpr,指向 const int
    std::cout << "constexpr const int* ptr = &value; // *ptr = " << *ptr << "\n";
    (void)b; (void)ptr;
    std::cout << "\n";
}

// ============================================================
// 第五部分:constexpr 與陣列
// ============================================================

// 編譯期產生乘法表
constexpr auto make_multiplication_table() {
    std::array<std::array<int, 10>, 10> table{};
    for (int i = 0; i < 10; ++i) {
        for (int j = 0; j < 10; ++j) {
            table[i][j] = (i + 1) * (j + 1);
        }
    }
    return table;
}

// 編譯期產生平方表
constexpr auto make_square_table() {
    std::array<int, 20> table{};
    for (int i = 0; i < 20; ++i) {
        table[i] = i * i;
    }
    return table;
}

// 編譯期字串長度計算
constexpr std::size_t string_length(const char* str) {
    std::size_t len = 0;
    while (str[len] != '\0') ++len;
    return len;
}

// 編譯期字元統計
constexpr int count_char(std::string_view sv, char c) {
    int count = 0;
    for (char ch : sv) {
        if (ch == c) ++count;
    }
    return count;
}

constexpr auto mult_table = make_multiplication_table();
constexpr auto square_table = make_square_table();

void demo_constexpr_arrays() {
    std::cout << "========================================\n";
    std::cout << "  constexpr 與陣列\n";
    std::cout << "========================================\n\n";

    // 印出部分乘法表
    std::cout << "編譯期乘法表(部分):\n";
    std::cout << "    ";
    for (int j = 0; j < 10; ++j) std::cout << (j + 1) << "\t";
    std::cout << "\n    " << std::string(80, '-') << "\n";
    for (int i = 0; i < 5; ++i) {
        std::cout << (i + 1) << " | ";
        for (int j = 0; j < 10; ++j) {
            std::cout << mult_table[i][j] << "\t";
        }
        std::cout << "\n";
    }
    std::cout << "  ... (後面省略)\n";

    // 平方表
    std::cout << "\n編譯期平方表:\n  ";
    for (int i = 0; i < 20; ++i) {
        std::cout << i << "²=" << square_table[i];
        if (i < 19) std::cout << ", ";
        if (i == 9) std::cout << "\n  ";
    }
    std::cout << "\n";

    // 編譯期字串操作
    constexpr auto len = string_length("Hello, World!");
    constexpr auto vowels = count_char("Hello World", 'l');
    static_assert(len == 13, "字串長度計算錯誤");

    std::cout << "\n編譯期字串操作:\n";
    std::cout << "  \"Hello, World!\" 長度 = " << len << "\n";
    std::cout << "  \"Hello World\" 中 'l' 出現 " << vowels << " 次\n";
    std::cout << "\n";
}

// ============================================================
// 第六部分:constexpr 階乘與費波那契表
// ============================================================

constexpr auto make_factorial_table() {
    std::array<long long, 21> table{};
    table[0] = 1;
    for (int i = 1; i <= 20; ++i) {
        table[i] = table[i - 1] * i;
    }
    return table;
}

constexpr auto make_fibonacci_table() {
    std::array<long long, 50> table{};
    table[0] = 0;
    table[1] = 1;
    for (int i = 2; i < 50; ++i) {
        table[i] = table[i - 1] + table[i - 2];
    }
    return table;
}

constexpr auto factorials = make_factorial_table();
constexpr auto fibonaccis = make_fibonacci_table();

static_assert(factorials[0] == 1);
static_assert(factorials[5] == 120);
static_assert(factorials[10] == 3628800);
static_assert(fibonaccis[10] == 55);

void demo_precomputed_tables() {
    std::cout << "========================================\n";
    std::cout << "  編譯期預計算表\n";
    std::cout << "========================================\n\n";

    std::cout << "階乘表(0! ~ 20!):\n";
    for (int i = 0; i <= 20; ++i) {
        std::cout << "  " << i << "! = " << factorials[i] << "\n";
    }

    std::cout << "\n費波那契數列(前 20 個):\n  ";
    for (int i = 0; i < 20; ++i) {
        std::cout << fibonaccis[i];
        if (i < 19) std::cout << ", ";
        if (i == 9) std::cout << "\n  ";
    }
    std::cout << "\n\n";

    std::cout << "這些表格在編譯期就已經計算完成,\n";
    std::cout << "執行期查詢只需要 O(1) 時間。\n";
    std::cout << "\n";
}

// ============================================================
// 主程式
// ============================================================

int main() {
    std::cout << "╔══════════════════════════════════════╗\n";
    std::cout << "║  C++17 constexpr 基礎                 ║\n";
    std::cout << "╚══════════════════════════════════════╝\n\n";

    demo_constexpr_variables();
    demo_constexpr_functions();
    demo_static_assert();
    demo_const_vs_constexpr();
    demo_constexpr_arrays();
    demo_precomputed_tables();

    std::cout << "=== 程式結束 ===\n";
    return 0;
}

관련 글