S SmartDocs
Chuỗi bài: C++ cpp 492 dòng · Cập nhật 2026-04-03

if_init_statements.cpp

C++/Part4_現代CPP/Ch20_CPP17特性精選/if_init_statements.cpp

// Ch20 範例:if 初始化語句、if constexpr、屬性、string_view
// 編譯:g++ -std=c++17 -Wall -o if_init_statements if_init_statements.cpp

#include <iostream>
#include <string>
#include <string_view>
#include <map>
#include <vector>
#include <optional>
#include <type_traits>
#include <cmath>
#include <algorithm>
#include <mutex>

// ============================================================
// 輔助函式
// ============================================================

void printSeparator(const std::string& title) {
    std::cout << "\n" << std::string(60, '=') << "\n";
    std::cout << title << "\n";
    std::cout << std::string(60, '=') << "\n";
}

void printSubSection(const std::string& title) {
    std::cout << "\n" << std::string(50, '-') << "\n";
    std::cout << title << "\n";
    std::cout << std::string(50, '-') << "\n";
}

// ============================================================
// Part 1: [[nodiscard]] 屬性範例
// ============================================================

// 標記回傳值不可忽略
[[nodiscard]] int computeChecksum(const std::string& data) {
    int sum = 0;
    for (char c : data)
        sum += static_cast<int>(c);
    return sum % 256;
}

// 可以標記在類別/結構上(C++17 擴展)
struct [[nodiscard]] ErrorCode {
    int code;
    std::string message;
};

ErrorCode validateInput(int value) {
    if (value < 0)
        return {-1, "值不可為負數"};
    if (value > 1000)
        return {-2, "值超出範圍"};
    return {0, "OK"};
}

// ============================================================
// Part 2: if constexpr 模板範例
// ============================================================

// 根據型別在編譯期選擇不同的處理方式
template <typename T>
std::string typeToString(const T& value) {
    if constexpr (std::is_integral_v<T>) {
        return "整數:" + std::to_string(value);
    } else if constexpr (std::is_floating_point_v<T>) {
        return "浮點數:" + std::to_string(value);
    } else if constexpr (std::is_same_v<T, std::string>) {
        return "字串:\"" + value + "\"";
    } else if constexpr (std::is_same_v<T, const char*>) {
        return "C 字串:\"" + std::string(value) + "\"";
    } else if constexpr (std::is_same_v<T, bool>) {
        return value ? "布林:true" : "布林:false";
    } else {
        return "未知型別";
    }
}

// 通用的 print 函式,根據型別自動選擇格式
template <typename T>
void smartPrint(const T& value) {
    if constexpr (std::is_arithmetic_v<T>) {
        std::cout << "[數值] " << value << "\n";
    } else if constexpr (std::is_same_v<std::decay_t<T>, std::string>) {
        std::cout << "[字串] \"" << value << "\" (長度: " << value.size() << ")\n";
    } else if constexpr (std::is_pointer_v<T>) {
        if (value)
            std::cout << "[指標] " << static_cast<const void*>(value) << "\n";
        else
            std::cout << "[指標] nullptr\n";
    } else {
        std::cout << "[物件] (無法直接印出)\n";
    }
}

// 安全地取得容器中的元素
template <typename Container>
auto getElement(const Container& c, size_t index)
    -> std::optional<typename Container::value_type>
{
    if (index < c.size()) {
        auto it = c.begin();
        std::advance(it, static_cast<long>(index));
        return *it;
    }
    return std::nullopt;
}

// ============================================================
// Part 3: 可變參數模板 + fold expression
// ============================================================

// 使用折疊表達式列印所有引數
template <typename... Args>
void printAll(const Args&... args) {
    ((std::cout << args << " "), ...);
    std::cout << "\n";
}

// 計算所有引數的總和
template <typename... Args>
auto sum(Args... args) {
    return (... + args);
}

// 檢查是否所有引數都符合條件
template <typename... Args>
bool allPositive(Args... args) {
    return (... && (args > 0));
}

// ============================================================
// Part 4: string_view 相關函式
// ============================================================

// 使用 string_view 避免不必要的拷貝
void analyzeString(std::string_view sv) {
    std::cout << "  內容:\"" << sv << "\"\n";
    std::cout << "  長度:" << sv.size() << "\n";
    std::cout << "  空?" << std::boolalpha << sv.empty() << "\n";

    if (!sv.empty()) {
        std::cout << "  第一個字元:'" << sv.front() << "'\n";
        std::cout << "  最後一個字元:'" << sv.back() << "'\n";
    }
}

// string_view 可以接受多種字串來源
size_t countChar(std::string_view sv, char ch) {
    size_t count = 0;
    for (char c : sv)
        if (c == ch) ++count;
    return count;
}

bool startsWith(std::string_view sv, std::string_view prefix) {
    return sv.size() >= prefix.size() &&
           sv.substr(0, prefix.size()) == prefix;
}

bool endsWith(std::string_view sv, std::string_view suffix) {
    return sv.size() >= suffix.size() &&
           sv.substr(sv.size() - suffix.size()) == suffix;
}

// ============================================================
// 主程式
// ============================================================
int main() {
    std::cout << std::string(60, '=') << "\n";
    std::cout << "C++17:if 初始化、if constexpr、屬性、string_view\n";
    std::cout << std::string(60, '=') << "\n";

    // =====================================================
    // Part 1: if 帶初始化語句
    // =====================================================
    printSeparator("Part 1: if / switch 帶初始化語句");

    // --- 1.1 if with initializer ---
    printSubSection("1.1 if 帶初始化");

    std::map<std::string, int> inventory = {
        {"蘋果", 50}, {"香蕉", 30}, {"橘子", 25}
    };

    // C++17 語法:if (初始化; 條件)
    // it 的作用域限制在 if-else 區塊內
    if (auto it = inventory.find("蘋果"); it != inventory.end()) {
        std::cout << "找到蘋果,庫存:" << it->second << "\n";
    } else {
        std::cout << "未找到蘋果\n";
    }

    if (auto it = inventory.find("西瓜"); it != inventory.end()) {
        std::cout << "找到西瓜,庫存:" << it->second << "\n";
    } else {
        std::cout << "未找到西瓜\n";
    }

    // 搭配 insert 的回傳值
    if (auto [it, inserted] = inventory.insert({"芒果", 15}); inserted) {
        std::cout << "成功新增:" << it->first << " = " << it->second << "\n";
    } else {
        std::cout << it->first << " 已存在,庫存:" << it->second << "\n";
    }

    // 搭配 std::optional
    auto findFruit = [&](const std::string& name) -> std::optional<int> {
        if (auto it = inventory.find(name); it != inventory.end())
            return it->second;
        return std::nullopt;
    };

    if (auto qty = findFruit("香蕉"); qty.has_value()) {
        std::cout << "香蕉庫存:" << *qty << "\n";
    }

    // --- 1.2 switch with initializer ---
    printSubSection("1.2 switch 帶初始化");

    auto getGrade = [](int score) -> char {
        // switch (初始化; 條件)
        switch (char grade = (score >= 90) ? 'A' :
                             (score >= 80) ? 'B' :
                             (score >= 70) ? 'C' :
                             (score >= 60) ? 'D' : 'F'; grade) {
            case 'A': std::cout << score << " 分 → 等級 A(優秀)\n"; break;
            case 'B': std::cout << score << " 分 → 等級 B(良好)\n"; break;
            case 'C': std::cout << score << " 分 → 等級 C(普通)\n"; break;
            case 'D': std::cout << score << " 分 → 等級 D(及格)\n"; break;
            case 'F': std::cout << score << " 分 → 等級 F(不及格)\n"; break;
        }
        return ' ';
    };

    getGrade(95);
    getGrade(82);
    getGrade(71);
    getGrade(55);

    // 搭配 mutex(實際應用場景)
    {
        std::mutex mtx;
        int sharedData = 42;

        // lock_guard 的作用域精確限制在 if 區塊內
        if (std::lock_guard lock(mtx); sharedData > 0) {
            std::cout << "\n鎖定後讀取共享資料:" << sharedData << "\n";
        }
        // lock_guard 在此自動釋放
    }

    // =====================================================
    // Part 2: if constexpr
    // =====================================================
    printSeparator("Part 2: if constexpr 編譯期分支");

    printSubSection("2.1 型別轉字串");

    std::cout << typeToString(42) << "\n";
    std::cout << typeToString(3.14) << "\n";
    std::cout << typeToString(std::string("Hello")) << "\n";
    std::cout << typeToString("World") << "\n";

    printSubSection("2.2 智慧列印");

    smartPrint(42);
    smartPrint(3.14159);
    smartPrint(std::string("Modern C++"));
    int x = 10;
    smartPrint(&x);
    int* nullp = nullptr;
    smartPrint(nullp);

    printSubSection("2.3 通用容器存取");

    std::vector<int> vec = {10, 20, 30, 40, 50};
    auto e1 = getElement(vec, 2);
    auto e2 = getElement(vec, 10);
    std::cout << "vec[2] = " << (e1 ? std::to_string(*e1) : "超出範圍") << "\n";
    std::cout << "vec[10] = " << (e2 ? std::to_string(*e2) : "超出範圍") << "\n";

    // =====================================================
    // Part 3: 屬性(Attributes)
    // =====================================================
    printSeparator("Part 3: C++17 屬性");

    // --- [[nodiscard]] ---
    printSubSection("3.1 [[nodiscard]]");

    auto checksum = computeChecksum("Hello C++17");
    std::cout << "校驗碼:" << checksum << "\n";

    // 如果取消以下注解,編譯器會發出警告
    // computeChecksum("test");  // ⚠️ 忽略了 [[nodiscard]] 回傳值

    auto err = validateInput(-5);
    std::cout << "驗證結果:code=" << err.code << ", msg=" << err.message << "\n";
    // validateInput(42);  // ⚠️ 忽略了 [[nodiscard]] 結構的回傳值

    // --- [[maybe_unused]] ---
    printSubSection("3.2 [[maybe_unused]]");

    [[maybe_unused]] int debugLevel = 3;        // 不會產生「未使用變數」警告
    [[maybe_unused]] bool verbose = true;

    // 在函式參數中使用
    auto process = []([[maybe_unused]] int reserved, int value) {
        return value * 2;
    };
    std::cout << "process(0, 21) = " << process(0, 21) << "\n";

    // --- [[fallthrough]] ---
    printSubSection("3.3 [[fallthrough]]");

    auto describeMonth = [](int month) {
        std::cout << month << " 月 → ";
        switch (month) {
            case 12:
            case 1:
            case 2:
                std::cout << "冬季";
                break;
            case 3:
            case 4:
            case 5:
                std::cout << "春季";
                break;
            case 6:
                std::cout << "夏季(開始)";
                [[fallthrough]];  // 明確表示要貫穿到下一個 case
            case 7:
            case 8:
                std::cout << "夏季";
                break;
            case 9:
            case 10:
            case 11:
                std::cout << "秋季";
                break;
            default:
                std::cout << "無效月份";
        }
        std::cout << "\n";
    };

    for (int m = 1; m <= 12; ++m) {
        describeMonth(m);
    }

    // =====================================================
    // Part 4: 折疊表達式(Fold Expressions)
    // =====================================================
    printSeparator("Part 4: 折疊表達式");

    printSubSection("4.1 printAll — 列印所有引數");
    printAll(1, 2.5, "hello", std::string("world"), 'A');

    printSubSection("4.2 sum — 計算總和");
    std::cout << "sum(1,2,3,4,5) = " << sum(1, 2, 3, 4, 5) << "\n";
    std::cout << "sum(1.1, 2.2, 3.3) = " << sum(1.1, 2.2, 3.3) << "\n";

    printSubSection("4.3 allPositive — 條件折疊");
    std::cout << "allPositive(1,2,3) = " << std::boolalpha
              << allPositive(1, 2, 3) << "\n";
    std::cout << "allPositive(1,-2,3) = "
              << allPositive(1, -2, 3) << "\n";

    // =====================================================
    // Part 5: std::string_view
    // =====================================================
    printSeparator("Part 5: std::string_view");

    // --- 基本建構 ---
    printSubSection("5.1 建構 string_view");

    // 從字串字面值建構(不配置記憶體)
    std::string_view sv1 = "Hello, string_view!";
    std::cout << "從字面值:\"" << sv1 << "\"\n";

    // 從 std::string 建構
    std::string str = "This is a std::string";
    std::string_view sv2 = str;
    std::cout << "從 std::string:\"" << sv2 << "\"\n";

    // 指定長度
    std::string_view sv3{"Hello, World!", 5};
    std::cout << "指定長度 5:\"" << sv3 << "\"\n\n";

    // --- 零拷貝子字串 ---
    printSubSection("5.2 零拷貝子字串(substr)");

    std::string_view fullPath = "/home/user/documents/report.txt";
    std::cout << "完整路徑:\"" << fullPath << "\"\n";

    // substr 回傳的也是 string_view,不會配置新記憶體
    auto dir = fullPath.substr(0, fullPath.rfind('/'));
    auto filename = fullPath.substr(fullPath.rfind('/') + 1);
    std::cout << "目錄:\"" << dir << "\"\n";
    std::cout << "檔名:\"" << filename << "\"\n";

    // remove_prefix / remove_suffix
    std::string_view greeting = ">>>Hello<<<";
    greeting.remove_prefix(3);  // 移除前 3 個字元
    greeting.remove_suffix(3);  // 移除後 3 個字元
    std::cout << "修剪後:\"" << greeting << "\"\n\n";

    // --- 分析字串 ---
    printSubSection("5.3 字串分析函式");

    // 同一個函式可以接受 const char*、string、string_view
    analyzeString("Hello from const char*");
    std::cout << "\n";

    std::string s = "Hello from std::string";
    analyzeString(s);
    std::cout << "\n";

    std::string_view sv = "Hello from string_view";
    analyzeString(sv);

    // --- 字元計數與前後綴 ---
    printSubSection("5.4 實用函式");

    std::string text = "C++ is a powerful programming language";
    std::cout << "文字:\"" << text << "\"\n";
    std::cout << "空格數量:" << countChar(text, ' ') << "\n";
    std::cout << "字母 a 數量:" << countChar(text, 'a') << "\n";
    std::cout << "以 'C++' 開頭?" << std::boolalpha << startsWith(text, "C++") << "\n";
    std::cout << "以 'Java' 開頭?" << startsWith(text, "Java") << "\n";
    std::cout << "以 'language' 結尾?" << endsWith(text, "language") << "\n";

    // --- 比較 ---
    printSubSection("5.5 比較操作");

    std::string_view a = "apple";
    std::string_view b = "banana";
    std::string_view c = "apple";

    std::cout << "\"" << a << "\" == \"" << c << "\"?" << (a == c) << "\n";
    std::cout << "\"" << a << "\" == \"" << b << "\"?" << (a == b) << "\n";
    std::cout << "\"" << a << "\" < \"" << b << "\"?" << (a < b) << "\n";

    // 可以直接與 std::string 和 const char* 比較
    std::string strApple = "apple";
    std::cout << "string_view 與 std::string 比較:" << (a == strApple) << "\n";

    // --- string_view 的注意事項 ---
    printSubSection("5.6 注意事項");

    std::cout << "⚠️ string_view 不保證以 null 結尾\n";
    std::cout << "⚠️ 不要用 string_view 持有臨時字串的參考\n";
    std::cout << "⚠️ string_view 不擁有資料,底層字串必須保持有效\n";
    std::cout << "\n";

    // 錯誤示範(概念說明,不要這樣做)
    std::cout << "正確用法:\n";
    std::cout << "  ✅ void func(std::string_view sv) { ... }\n";
    std::cout << "  ✅ 函式參數使用 string_view 取代 const string&\n";
    std::cout << "  ❌ std::string_view sv = getString();  // 如果回傳臨時 string\n";
    std::cout << "  ❌ 儲存 string_view 為類別成員(除非確定生命週期)\n";

    // =====================================================
    // 總結
    // =====================================================
    printSeparator("總結");

    std::cout << R"(
C++17 為日常程式設計帶來了許多便利的改進:

  if/switch 帶初始化:
    • 將變數作用域限制在分支區塊內
    • 讓程式碼更簡潔、更安全

  if constexpr:
    • 編譯期分支判斷
    • 取代 SFINAE 和 tag dispatch 的部分場景
    • 讓模板程式碼更直觀

  屬性:
    • [[nodiscard]] — 防止忽略重要的回傳值
    • [[maybe_unused]] — 消除未使用變數的警告
    • [[fallthrough]] — 明確標記 switch 貫穿

  std::string_view:
    • 零拷貝的字串檢視
    • 適合用於函式參數
    • 注意生命週期管理
)";

    return 0;
}

Bài viết liên quan