S SmartDocs
Serie: C++ cpp 431 righe · Aggiornato 2026-04-03

optional_variant_any.cpp

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

// Ch20 範例:std::optional、std::variant、std::any
// 編譯:g++ -std=c++17 -Wall -o optional_variant_any optional_variant_any.cpp

#include <iostream>
#include <string>
#include <optional>
#include <variant>
#include <any>
#include <vector>
#include <map>
#include <cmath>

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

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: std::optional 範例函式
// ============================================================

// 安全除法:除數為零時回傳 nullopt
std::optional<double> safeDivide(double a, double b) {
    if (b == 0.0) return std::nullopt;
    return a / b;
}

// 在字串中尋找子字串,回傳位置
std::optional<size_t> findSubstring(const std::string& str,
                                     const std::string& sub) {
    auto pos = str.find(sub);
    if (pos == std::string::npos) return std::nullopt;
    return pos;
}

// 模擬使用者查詢
struct UserProfile {
    std::string name;
    int age;
    std::string email;
};

std::optional<UserProfile> findUser(const std::string& username) {
    // 模擬資料庫查詢
    std::map<std::string, UserProfile> db = {
        {"alice", {"Alice Wang", 25, "alice@example.com"}},
        {"bob",   {"Bob Chen",   30, "bob@example.com"}},
    };

    auto it = db.find(username);
    if (it != db.end()) return it->second;
    return std::nullopt;
}

// 平方根:負數時回傳 nullopt
std::optional<double> safeSqrt(double x) {
    if (x < 0.0) return std::nullopt;
    return std::sqrt(x);
}

// ============================================================
// Part 2: std::variant 輔助類別
// ============================================================

// 用 variant 表示 JSON 值的簡化版本
using JsonValue = std::variant<std::nullptr_t, bool, int, double, std::string>;

std::string jsonTypeToString(const JsonValue& jv) {
    return std::visit([](auto&& arg) -> std::string {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, std::nullptr_t>) return "null";
        else if constexpr (std::is_same_v<T, bool>)       return "boolean";
        else if constexpr (std::is_same_v<T, int>)        return "integer";
        else if constexpr (std::is_same_v<T, double>)     return "number";
        else if constexpr (std::is_same_v<T, std::string>) return "string";
        else return "unknown";
    }, jv);
}

void printJsonValue(const JsonValue& jv) {
    std::visit([](auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, std::nullptr_t>)
            std::cout << "null";
        else if constexpr (std::is_same_v<T, bool>)
            std::cout << std::boolalpha << arg;
        else
            std::cout << arg;
    }, jv);
}

// 用 variant 表示運算結果(成功或錯誤訊息)
using CalcResult = std::variant<double, std::string>;

CalcResult calculate(double a, const std::string& op, double b) {
    if (op == "+") return a + b;
    if (op == "-") return a - b;
    if (op == "*") return a * b;
    if (op == "/") {
        if (b == 0.0) return std::string("錯誤:除數為零");
        return a / b;
    }
    return std::string("錯誤:未知運算子 '" + op + "'");
}

// ============================================================
// Part 3: std::any 配置系統
// ============================================================

class Config {
    std::map<std::string, std::any> settings;
public:
    template <typename T>
    void set(const std::string& key, T value) {
        settings[key] = std::move(value);
    }

    template <typename T>
    std::optional<T> get(const std::string& key) const {
        auto it = settings.find(key);
        if (it == settings.end()) return std::nullopt;
        try {
            return std::any_cast<T>(it->second);
        } catch (const std::bad_any_cast&) {
            return std::nullopt;
        }
    }

    bool has(const std::string& key) const {
        return settings.count(key) > 0;
    }

    std::string typeName(const std::string& key) const {
        auto it = settings.find(key);
        if (it == settings.end()) return "(不存在)";
        return it->second.type().name();
    }
};

// ============================================================
// 主程式
// ============================================================
int main() {
    // =====================================================
    // Part 1: std::optional
    // =====================================================
    printSeparator("Part 1: std::optional");

    // --- 基本使用 ---
    printSubSection("1.1 基本建構與檢查");

    std::optional<int> opt1;                 // 空的 optional
    std::optional<int> opt2 = 42;            // 有值
    std::optional<std::string> opt3 = "Hi";  // 隱式轉換

    std::cout << "opt1 有值?" << std::boolalpha << opt1.has_value() << "\n";
    std::cout << "opt2 有值?" << opt2.has_value() << ",值 = " << opt2.value() << "\n";
    std::cout << "opt3 有值?" << opt3.has_value() << ",值 = " << opt3.value() << "\n";

    // 使用 bool 轉換
    if (opt2) {
        std::cout << "opt2 可以直接當 bool 使用\n";
    }

    // --- value_or ---
    printSubSection("1.2 value_or() 提供預設值");

    std::optional<int> empty;
    std::optional<int> filled = 100;

    std::cout << "empty.value_or(-1) = " << empty.value_or(-1) << "\n";
    std::cout << "filled.value_or(-1) = " << filled.value_or(-1) << "\n";

    // --- 安全除法 ---
    printSubSection("1.3 安全除法(實際應用)");

    auto r1 = safeDivide(10.0, 3.0);
    auto r2 = safeDivide(10.0, 0.0);

    std::cout << "10 / 3 = ";
    if (r1) std::cout << *r1 << "\n";  // 使用 * 解參考
    else    std::cout << "無法計算\n";

    std::cout << "10 / 0 = ";
    if (r2) std::cout << *r2 << "\n";
    else    std::cout << "無法計算(除數為零)\n";

    // --- 尋找子字串 ---
    printSubSection("1.4 尋找子字串");

    std::string text = "C++17 引入了 std::optional";
    auto pos1 = findSubstring(text, "optional");
    auto pos2 = findSubstring(text, "variant");

    std::cout << "在「" << text << "」中\n";
    std::cout << "  尋找 'optional':" << (pos1 ? "位置 " + std::to_string(*pos1) : "未找到") << "\n";
    std::cout << "  尋找 'variant':" << (pos2 ? "位置 " + std::to_string(*pos2) : "未找到") << "\n";

    // --- 使用者查詢 ---
    printSubSection("1.5 模擬使用者查詢");

    for (const auto& uname : {"alice", "bob", "charlie"}) {
        auto user = findUser(uname);
        std::cout << "查詢 '" << uname << "':";
        if (user) {
            std::cout << user->name << "," << user->age << " 歲,"
                      << user->email << "\n";
        } else {
            std::cout << "使用者不存在\n";
        }
    }

    // --- 平方根 ---
    printSubSection("1.6 安全平方根");

    for (double val : {16.0, 2.0, 0.0, -4.0}) {
        auto result = safeSqrt(val);
        std::cout << "sqrt(" << val << ") = "
                  << (result ? std::to_string(*result) : "undefined(負數)") << "\n";
    }

    // =====================================================
    // Part 2: std::variant
    // =====================================================
    printSeparator("Part 2: std::variant");

    // --- 基本使用 ---
    printSubSection("2.1 基本建構與取值");

    std::variant<int, double, std::string> v;
    v = 42;
    std::cout << "存入 int 42 → std::get<int>(v) = " << std::get<int>(v) << "\n";

    v = 3.14;
    std::cout << "存入 double 3.14 → std::get<double>(v) = " << std::get<double>(v) << "\n";

    v = std::string("Hello C++17");
    std::cout << "存入 string → std::get<string>(v) = " << std::get<std::string>(v) << "\n";

    // --- holds_alternative ---
    printSubSection("2.2 holds_alternative 型別檢查");

    std::cout << "目前是 int?" << std::boolalpha
              << std::holds_alternative<int>(v) << "\n";
    std::cout << "目前是 string?"
              << std::holds_alternative<std::string>(v) << "\n";

    // --- std::get 失敗 ---
    printSubSection("2.3 std::get 失敗處理");
    try {
        [[maybe_unused]] auto bad = std::get<int>(v);  // 目前是 string
    } catch (const std::bad_variant_access& e) {
        std::cout << "捕獲 bad_variant_access:" << e.what() << "\n";
    }

    // 使用 get_if 安全取值(回傳指標)
    if (auto* pStr = std::get_if<std::string>(&v)) {
        std::cout << "get_if<string> 成功:" << *pStr << "\n";
    }
    if (auto* pInt = std::get_if<int>(&v)) {
        std::cout << "get_if<int> 成功:" << *pInt << "\n";
    } else {
        std::cout << "get_if<int> 回傳 nullptr(型別不符)\n";
    }

    // --- std::visit ---
    printSubSection("2.4 std::visit 搭配 lambda");

    std::variant<int, double, std::string> v2 = 42;

    // 泛型 lambda
    auto visitor = [](auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int>)
            std::cout << "  int 值:" << arg << "\n";
        else if constexpr (std::is_same_v<T, double>)
            std::cout << "  double 值:" << arg << "\n";
        else if constexpr (std::is_same_v<T, std::string>)
            std::cout << "  string 值:\"" << arg << "\"\n";
    };

    std::cout << "訪問各種型別:\n";
    v2 = 42;          std::visit(visitor, v2);
    v2 = 2.718;       std::visit(visitor, v2);
    v2 = std::string("Modern C++");  std::visit(visitor, v2);

    // --- JSON 值範例 ---
    printSubSection("2.5 實際應用:簡易 JSON 值");

    std::vector<JsonValue> jsonData = {
        nullptr, true, 42, 3.14, std::string("hello")
    };

    std::cout << "JSON 資料:\n";
    for (const auto& jv : jsonData) {
        std::cout << "  型別:" << jsonTypeToString(jv) << ",值:";
        printJsonValue(jv);
        std::cout << "\n";
    }

    // --- 計算器範例 ---
    printSubSection("2.6 實際應用:計算器");

    struct Operation {
        double a;
        std::string op;
        double b;
    };

    std::vector<Operation> operations = {
        {10, "+", 5}, {10, "-", 3}, {10, "*", 4},
        {10, "/", 3}, {10, "/", 0}, {10, "%", 3}
    };

    for (const auto& [a, op, b] : operations) {
        auto result = calculate(a, op, b);
        std::cout << "  " << a << " " << op << " " << b << " = ";
        std::visit([](auto&& arg) {
            using T = std::decay_t<decltype(arg)>;
            if constexpr (std::is_same_v<T, double>)
                std::cout << arg;
            else
                std::cout << arg;  // 錯誤訊息
        }, result);
        std::cout << "\n";
    }

    // =====================================================
    // Part 3: std::any
    // =====================================================
    printSeparator("Part 3: std::any");

    // --- 基本使用 ---
    printSubSection("3.1 基本建構與取值");

    std::any a;
    std::cout << "空的 any 有值?" << std::boolalpha << a.has_value() << "\n";

    a = 42;
    std::cout << "存入 42 → type: " << a.type().name()
              << ",值:" << std::any_cast<int>(a) << "\n";

    a = 3.14;
    std::cout << "存入 3.14 → type: " << a.type().name()
              << ",值:" << std::any_cast<double>(a) << "\n";

    a = std::string("C++17");
    std::cout << "存入 \"C++17\" → type: " << a.type().name()
              << ",值:" << std::any_cast<std::string>(a) << "\n";

    // --- any_cast 失敗 ---
    printSubSection("3.2 any_cast 失敗處理");
    try {
        [[maybe_unused]] auto bad = std::any_cast<int>(a);  // 目前是 string
    } catch (const std::bad_any_cast& e) {
        std::cout << "捕獲 bad_any_cast:" << e.what() << "\n";
    }

    // 安全的 any_cast(回傳指標)
    if (auto* p = std::any_cast<std::string>(&a)) {
        std::cout << "any_cast<string> 成功:" << *p << "\n";
    }

    // --- 重置 ---
    a.reset();
    std::cout << "reset 後有值?" << a.has_value() << "\n";

    // --- 配置系統範例 ---
    printSubSection("3.3 實際應用:配置系統");

    Config config;
    config.set("app_name", std::string("MyApp"));
    config.set("version", 2);
    config.set("pi", 3.14159);
    config.set("debug_mode", true);
    config.set("max_connections", 100);

    std::cout << "app_name = "
              << config.get<std::string>("app_name").value_or("N/A") << "\n";
    std::cout << "version = "
              << config.get<int>("version").value_or(0) << "\n";
    std::cout << "pi = "
              << config.get<double>("pi").value_or(0.0) << "\n";
    std::cout << "debug_mode = " << std::boolalpha
              << config.get<bool>("debug_mode").value_or(false) << "\n";
    std::cout << "max_connections = "
              << config.get<int>("max_connections").value_or(0) << "\n";
    std::cout << "nonexistent = "
              << config.get<int>("nonexistent").value_or(-1)
              << "(預設值)\n";

    // 型別不符
    auto wrongType = config.get<std::string>("version");
    std::cout << "get<string>(\"version\") = "
              << (wrongType ? *wrongType : "(型別不符,取得失敗)") << "\n";

    // =====================================================
    // 總結:何時使用哪個?
    // =====================================================
    printSeparator("總結:optional vs variant vs any");

    std::cout << R"(
┌─────────────────┬─────────────────────────────────────┐
│ 型別            │ 使用時機                            │
├─────────────────┼─────────────────────────────────────┤
│ std::optional   │ 值可能存在也可能不存在              │
│                 │ 取代 -1/nullptr 等特殊值            │
├─────────────────┼─────────────────────────────────────┤
│ std::variant    │ 值是有限幾種型別之一                │
│                 │ 型別安全的 union                    │
├─────────────────┼─────────────────────────────────────┤
│ std::any        │ 值可以是任何型別                    │
│                 │ 最靈活但最不安全,盡量少用          │
└─────────────────┴─────────────────────────────────────┘

推薦優先順序:optional > variant > any
)";

    return 0;
}

Articoli correlati