S SmartDocs
シリーズ: C++ cpp 371 行 · 更新日 2026-04-03

std_function.cpp

C++/Part3_泛型與STL/Ch15_Lambda與函式物件/std_function.cpp

// Ch15 — std::function 示範
// 編譯:g++ -std=c++17 -Wall -o std_function std_function.cpp

#include <iostream>
#include <functional>  // std::function
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <iomanip>
#include <cmath>

// 輔助函式:印出分隔線
void section(const std::string& title) {
    std::cout << "\n===== " << title << " =====\n";
}

// ============================================================
// 普通函式(用來示範 std::function 儲存普通函式)
// ============================================================
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }

double squareRoot(double x) { return std::sqrt(x); }

// ============================================================
// 函式物件(用來示範 std::function 儲存函式物件)
// ============================================================
class Multiplier {
    int factor_;
public:
    explicit Multiplier(int factor) : factor_(factor) {}
    int operator()(int x) const { return x * factor_; }
};

// ============================================================
// 事件處理器類別
// ============================================================
class EventSystem {
    // 儲存事件名稱對應的回呼函式列表
    std::map<std::string, std::vector<std::function<void(const std::string&)>>> handlers_;

public:
    // 註冊事件處理器
    void on(const std::string& event,
            std::function<void(const std::string&)> handler) {
        handlers_[event].push_back(std::move(handler));
    }

    // 觸發事件
    void emit(const std::string& event, const std::string& data = "") {
        auto it = handlers_.find(event);
        if (it != handlers_.end()) {
            std::cout << "[事件觸發] " << event << "\n";
            for (const auto& handler : it->second) {
                handler(data);
            }
        } else {
            std::cout << "[事件觸發] " << event << "(無處理器)\n";
        }
    }
};

// ============================================================
// 簡易計算機類別
// ============================================================
class Calculator {
    std::map<std::string, std::function<double(double, double)>> operations_;
    std::vector<std::string> history_;

public:
    Calculator() {
        operations_["+"] = [](double a, double b) { return a + b; };
        operations_["-"] = [](double a, double b) { return a - b; };
        operations_["*"] = [](double a, double b) { return a * b; };
        operations_["/"] = [](double a, double b) {
            if (b == 0) throw std::runtime_error("除以零");
            return a / b;
        };
        operations_["^"] = [](double a, double b) { return std::pow(a, b); };
        operations_["%"] = [](double a, double b) {
            return std::fmod(a, b);
        };
    }

    // 新增自訂運算
    void addOperation(const std::string& name,
                      std::function<double(double, double)> op) {
        operations_[name] = std::move(op);
    }

    double calculate(const std::string& op, double a, double b) {
        auto it = operations_.find(op);
        if (it == operations_.end()) {
            throw std::runtime_error("未知運算:" + op);
        }
        double result = it->second(a, b);
        history_.push_back(std::to_string(a) + " " + op + " "
                          + std::to_string(b) + " = " + std::to_string(result));
        return result;
    }

    void printHistory() const {
        std::cout << "計算歷史:\n";
        for (const auto& entry : history_) {
            std::cout << "  " << entry << "\n";
        }
    }

    void listOperations() const {
        std::cout << "支援的運算:";
        for (const auto& [name, _] : operations_) {
            std::cout << name << " ";
        }
        std::cout << "\n";
    }
};

int main() {
    // ========================================================
    // 1. std::function 基本用法
    // ========================================================
    section("1. std::function 基本用法");

    // 儲存 lambda
    std::function<int(int, int)> op = [](int a, int b) { return a + b; };
    std::cout << "Lambda 加法:" << op(3, 4) << "\n";

    // 儲存普通函式
    op = add;
    std::cout << "普通函式 add:" << op(3, 4) << "\n";

    op = subtract;
    std::cout << "普通函式 subtract:" << op(10, 3) << "\n";

    // 儲存函式物件
    std::function<int(int)> triple = Multiplier(3);
    std::cout << "函式物件 Multiplier(3):" << triple(7) << "\n";

    // 檢查 std::function 是否有效
    std::function<void()> empty;
    std::cout << "empty 是否有效:" << (empty ? "是" : "否") << "\n";

    empty = []() { std::cout << "現在有效了!\n"; };
    std::cout << "賦值後是否有效:" << (empty ? "是" : "否") << "\n";
    empty();

    // ========================================================
    // 2. std::function 作為函式參數(callback)
    // ========================================================
    section("2. std::function 作為 callback");

    // 對 vector 的每個元素套用轉換,並印出結果
    auto applyAndPrint = [](const std::vector<int>& v,
                            std::function<int(int)> func,
                            const std::string& desc) {
        std::cout << desc << ":";
        for (int x : v) {
            std::cout << func(x) << " ";
        }
        std::cout << "\n";
    };

    std::vector<int> nums = {1, 2, 3, 4, 5};
    applyAndPrint(nums, [](int x) { return x * x; }, "平方");
    applyAndPrint(nums, [](int x) { return x * 2; }, "雙倍");
    applyAndPrint(nums, [](int x) { return x + 100; }, "加 100");
    applyAndPrint(nums, Multiplier(5), "乘 5");

    // 接受二元函式的高階函式
    auto reduce = [](const std::vector<int>& v, int init,
                     std::function<int(int, int)> op) -> int {
        int result = init;
        for (int x : v) {
            result = op(result, x);
        }
        return result;
    };

    std::cout << "加法 reduce:" << reduce(nums, 0, add) << "\n";
    std::cout << "乘法 reduce:" << reduce(nums, 1, multiply) << "\n";
    std::cout << "最大值 reduce:" << reduce(nums, nums[0],
        [](int a, int b) { return std::max(a, b); }) << "\n";

    // ========================================================
    // 3. std::function 在容器中
    // ========================================================
    section("3. std::function 在容器中");

    // map:運算名稱 → 函式
    std::map<std::string, std::function<double(double, double)>> mathOps = {
        {"加法", [](double a, double b) { return a + b; }},
        {"減法", [](double a, double b) { return a - b; }},
        {"乘法", [](double a, double b) { return a * b; }},
        {"除法", [](double a, double b) { return b != 0 ? a / b : 0; }},
        {"次方", [](double a, double b) { return std::pow(a, b); }}
    };

    double x = 10.0, y = 3.0;
    std::cout << "x = " << x << ", y = " << y << "\n";
    for (const auto& [name, func] : mathOps) {
        std::cout << "  " << name << ":" << std::fixed
                  << std::setprecision(2) << func(x, y) << "\n";
    }

    // vector:儲存一系列轉換函式
    std::vector<std::function<double(double)>> pipeline = {
        [](double x) { return x * 2; },         // 乘 2
        [](double x) { return x + 10; },         // 加 10
        [](double x) { return std::sqrt(x); },   // 開根號
        [](double x) { return std::round(x * 100) / 100; }  // 取到小數第二位
    };

    double val = 8.0;
    std::cout << "\n管線處理:起始值 = " << val << "\n";
    for (size_t i = 0; i < pipeline.size(); i++) {
        val = pipeline[i](val);
        std::cout << "  步驟 " << (i + 1) << ":" << val << "\n";
    }

    // ========================================================
    // 4. 事件處理器模式
    // ========================================================
    section("4. 事件處理器模式");

    EventSystem events;

    // 註冊多個處理器
    events.on("click", [](const std::string& data) {
        std::cout << "  處理器 A:收到點擊事件,資料 = " << data << "\n";
    });

    events.on("click", [](const std::string& data) {
        std::cout << "  處理器 B:記錄點擊事件到日誌\n";
    });

    events.on("submit", [](const std::string& data) {
        std::cout << "  表單提交處理:" << data << "\n";
    });

    events.on("submit", [](const std::string& data) {
        std::cout << "  驗證資料:" << data << "\n";
    });

    events.on("error", [](const std::string& data) {
        std::cout << "  *** 錯誤處理:" << data << " ***\n";
    });

    // 觸發事件
    events.emit("click", "按鈕 A");
    std::cout << "\n";
    events.emit("submit", "使用者表單");
    std::cout << "\n";
    events.emit("error", "網路連線逾時");
    std::cout << "\n";
    events.emit("unknown");

    // ========================================================
    // 5. 計算機範例
    // ========================================================
    section("5. 可擴充計算機");

    Calculator calc;
    calc.listOperations();

    std::cout << "10 + 3 = " << calc.calculate("+", 10, 3) << "\n";
    std::cout << "10 - 3 = " << calc.calculate("-", 10, 3) << "\n";
    std::cout << "10 * 3 = " << calc.calculate("*", 10, 3) << "\n";
    std::cout << "10 / 3 = " << std::fixed << std::setprecision(4)
              << calc.calculate("/", 10, 3) << "\n";
    std::cout << "2 ^ 10 = " << calc.calculate("^", 2, 10) << "\n";

    // 新增自訂運算
    calc.addOperation("max", [](double a, double b) {
        return std::max(a, b);
    });
    calc.addOperation("min", [](double a, double b) {
        return std::min(a, b);
    });

    std::cout << "max(7, 3) = " << calc.calculate("max", 7, 3) << "\n";
    std::cout << "min(7, 3) = " << calc.calculate("min", 7, 3) << "\n";

    std::cout << "\n";
    calc.printHistory();

    // ========================================================
    // 6. std::function 與策略模式
    // ========================================================
    section("6. 策略模式(Strategy Pattern)");

    // 不同的折扣策略
    using DiscountStrategy = std::function<double(double)>;

    DiscountStrategy noDiscount = [](double price) {
        return price;
    };

    DiscountStrategy percentOff = [](double price) {
        return price * 0.8;  // 八折
    };

    DiscountStrategy fixedOff = [](double price) {
        return std::max(0.0, price - 50.0);  // 減 50 元
    };

    DiscountStrategy memberDiscount = [](double price) {
        if (price >= 500) return price * 0.7;       // 滿 500 打七折
        if (price >= 200) return price * 0.85;      // 滿 200 打八五折
        return price * 0.95;                        // 基本九五折
    };

    // 計算各策略下的價格
    std::map<std::string, DiscountStrategy> strategies = {
        {"無折扣", noDiscount},
        {"八折", percentOff},
        {"減 50 元", fixedOff},
        {"會員折扣", memberDiscount}
    };

    std::vector<double> testPrices = {100, 250, 500, 1000};

    std::cout << std::setw(12) << "原價";
    for (const auto& [name, _] : strategies) {
        std::cout << std::setw(12) << name;
    }
    std::cout << "\n" << std::string(60, '-') << "\n";

    for (double price : testPrices) {
        std::cout << std::setw(10) << std::fixed << std::setprecision(0)
                  << "$" << price;
        for (const auto& [_, strategy] : strategies) {
            std::cout << std::setw(10) << "$"
                      << std::setprecision(0) << strategy(price);
        }
        std::cout << "\n";
    }

    // ========================================================
    // 7. 函式組合
    // ========================================================
    section("7. 函式組合(compose)");

    // compose(f, g) 回傳一個函式 h(x) = f(g(x))
    auto compose = [](std::function<double(double)> f,
                      std::function<double(double)> g) {
        return [f, g](double x) -> double {
            return f(g(x));
        };
    };

    std::function<double(double)> doubleIt = [](double x) { return x * 2; };
    std::function<double(double)> addTen = [](double x) { return x + 10; };
    std::function<double(double)> sqrtIt = squareRoot;

    // 組合:先乘2,再加10
    auto doubleThenAdd = compose(addTen, doubleIt);
    std::cout << "doubleThenAdd(5) = (5*2)+10 = " << doubleThenAdd(5) << "\n";

    // 組合:先加10,再乘2
    auto addThenDouble = compose(doubleIt, addTen);
    std::cout << "addThenDouble(5) = (5+10)*2 = " << addThenDouble(5) << "\n";

    // 三層組合:先乘2,再加10,最後開根號
    auto combined = compose(sqrtIt, compose(addTen, doubleIt));
    std::cout << "combined(8) = sqrt((8*2)+10) = sqrt(26) ≈ "
              << std::setprecision(4) << combined(8) << "\n";

    return 0;
}

関連記事