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

function_objects.cpp

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

// Ch15 — 函式物件(Functors)示範
// 編譯:g++ -std=c++17 -Wall -o function_objects function_objects.cpp

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>  // std::plus, std::greater, std::multiplies, etc.
#include <string>
#include <iomanip>

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

template<typename T>
void printVec(const std::string& label, const std::vector<T>& v) {
    std::cout << label;
    for (const auto& x : v) std::cout << x << " ";
    std::cout << "\n";
}

// ============================================================
// 1. 基本函式物件:重載 operator()
// ============================================================
class Adder {
    int offset_;
public:
    explicit Adder(int offset) : offset_(offset) {}

    int operator()(int x) const {
        return x + offset_;
    }
};

// ============================================================
// 2. 有狀態的函式物件:計數器
// ============================================================
class Counter {
    int count_ = 0;
public:
    void operator()() {
        ++count_;
    }

    // 用於 for_each 時接受元素參數
    void operator()(int) {
        ++count_;
    }

    int getCount() const { return count_; }
};

// ============================================================
// 3. 有狀態的函式物件:累加器
// ============================================================
class Accumulator {
    double total_ = 0.0;
public:
    void operator()(double value) {
        total_ += value;
    }

    double getTotal() const { return total_; }
};

// ============================================================
// 4. 謂詞函式物件(用於條件判斷)
// ============================================================
class InRange {
    int low_, high_;
public:
    InRange(int low, int high) : low_(low), high_(high) {}

    bool operator()(int x) const {
        return x >= low_ && x <= high_;
    }
};

// ============================================================
// 5. 比較函式物件(用於排序)
// ============================================================
struct Student {
    std::string name;
    int score;
};

class CompareByScore {
    bool descending_;
public:
    explicit CompareByScore(bool desc = true) : descending_(desc) {}

    bool operator()(const Student& a, const Student& b) const {
        return descending_ ? a.score > b.score : a.score < b.score;
    }
};

// ============================================================
// 6. 普通函式(用於比較)
// ============================================================
int doubleValue(int x) {
    return x * 2;
}

bool isPositive(int x) {
    return x > 0;
}

int main() {
    // ========================================================
    // 1. 基本函式物件
    // ========================================================
    section("1. 基本函式物件");

    Adder add5(5);
    Adder add10(10);

    std::cout << "add5(3)  = " << add5(3) << "\n";
    std::cout << "add5(7)  = " << add5(7) << "\n";
    std::cout << "add10(3) = " << add10(3) << "\n";

    // 搭配 transform 使用
    std::vector<int> nums = {1, 2, 3, 4, 5};
    std::vector<int> result(nums.size());
    std::transform(nums.begin(), nums.end(), result.begin(), Adder(100));
    printVec("原始:", nums);
    printVec("加 100:", result);

    // ========================================================
    // 2. 有狀態的函式物件
    // ========================================================
    section("2. 有狀態的函式物件(Counter)");

    // for_each 回傳函式物件的副本,可用來取得最終狀態
    std::vector<int> data = {10, 20, 30, 40, 50};
    Counter c = std::for_each(data.begin(), data.end(), Counter());
    std::cout << "for_each 遍歷了 " << c.getCount() << " 個元素\n";

    section("有狀態的函式物件(Accumulator)");

    std::vector<double> prices = {29.99, 15.50, 42.00, 8.75, 33.25};
    Accumulator acc = std::for_each(prices.begin(), prices.end(), Accumulator());
    std::cout << "商品價格:";
    for (double p : prices) std::cout << "$" << p << " ";
    std::cout << "\n";
    std::cout << "總價:$" << std::fixed << std::setprecision(2)
              << acc.getTotal() << "\n";

    // ========================================================
    // 3. 謂詞函式物件
    // ========================================================
    section("3. 謂詞函式物件(InRange)");

    std::vector<int> scores = {45, 72, 88, 55, 91, 63, 78, 50, 95, 82};
    printVec("全部分數:", scores);

    // 計算 70-89 分的人數
    InRange bRange(70, 89);
    int bCount = std::count_if(scores.begin(), scores.end(), bRange);
    std::cout << "70-89 分(B 等)有 " << bCount << " 人\n";

    // 篩選 60-100 分(及格)的分數
    InRange passRange(60, 100);
    std::vector<int> passed;
    std::copy_if(scores.begin(), scores.end(),
        std::back_inserter(passed), passRange);
    printVec("及格分數:", passed);

    // 可以用不同參數建立不同範圍的謂詞
    InRange excellent(90, 100);
    int excellentCount = std::count_if(scores.begin(), scores.end(), excellent);
    std::cout << "90-100 分(優秀)有 " << excellentCount << " 人\n";

    // ========================================================
    // 4. 比較函式物件用於排序
    // ========================================================
    section("4. 比較函式物件用於排序");

    std::vector<Student> students = {
        {"Alice", 88}, {"Bob", 95}, {"Charlie", 72},
        {"Diana", 88}, {"Eve", 91}
    };

    // 分數降序
    std::sort(students.begin(), students.end(), CompareByScore(true));
    std::cout << "降序排列:\n";
    for (const auto& s : students) {
        std::cout << "  " << std::setw(8) << s.name << ":" << s.score << "\n";
    }

    // 分數升序
    std::sort(students.begin(), students.end(), CompareByScore(false));
    std::cout << "升序排列:\n";
    for (const auto& s : students) {
        std::cout << "  " << std::setw(8) << s.name << ":" << s.score << "\n";
    }

    // ========================================================
    // 5. 預定義函式物件
    // ========================================================
    section("5. 預定義函式物件(<functional>)");

    std::vector<int> v = {5, 2, 8, 1, 9, 3, 7};
    printVec("原始:", v);

    // std::greater — 降序排列
    std::sort(v.begin(), v.end(), std::greater<int>());
    printVec("greater 降序:", v);

    // std::less — 升序排列(預設行為)
    std::sort(v.begin(), v.end(), std::less<int>());
    printVec("less 升序:", v);

    // std::plus — 累加
    int sum = std::accumulate(v.begin(), v.end(), 0, std::plus<int>());
    std::cout << "plus 累加:" << sum << "\n";

    // std::multiplies — 累乘
    int product = std::accumulate(v.begin(), v.end(), 1, std::multiplies<int>());
    std::cout << "multiplies 累乘:" << product << "\n";

    // std::negate — 取負
    std::vector<int> negated(v.size());
    std::transform(v.begin(), v.end(), negated.begin(), std::negate<int>());
    printVec("negate 取負:", negated);

    // C++14 透明版本(不需指定型別)
    std::sort(v.begin(), v.end(), std::greater<>());
    printVec("greater<>() 降序:", v);

    // ========================================================
    // 6. 普通函式 vs 函式物件 vs Lambda 比較
    // ========================================================
    section("6. 三種方式比較");

    std::vector<int> values = {1, 2, 3, 4, 5};
    std::vector<int> out1(5), out2(5), out3(5);

    // 方式 1:普通函式
    std::transform(values.begin(), values.end(), out1.begin(), doubleValue);
    printVec("普通函式 doubleValue:", out1);

    // 方式 2:函式物件
    std::transform(values.begin(), values.end(), out2.begin(), Adder(0));
    // Adder(0) 加 0 等於複製,這裡用 multiplies 示範
    std::transform(values.begin(), values.end(), out2.begin(),
        [](int x) { return x * 2; });
    printVec("函式物件(lambda):", out2);

    // 方式 3:Lambda
    std::transform(values.begin(), values.end(), out3.begin(),
        [](int x) { return x * 2; });
    printVec("Lambda:", out3);

    std::cout << "\n比較摘要:\n";
    std::cout << "  普通函式:簡單但無法攜帶狀態\n";
    std::cout << "  函式物件:可攜帶狀態,可複用,編譯器容易內聯\n";
    std::cout << "  Lambda:  語法簡潔,適合一次性使用,C++14 起支援泛型\n";

    // ========================================================
    // 7. 綜合範例:多層篩選與轉換
    // ========================================================
    section("7. 綜合範例");

    std::vector<int> rawData = {-5, 12, -3, 8, 0, -1, 15, 7, -9, 20, 3};
    printVec("原始資料:", rawData);

    // 使用普通函式篩選正數
    std::vector<int> positives;
    std::copy_if(rawData.begin(), rawData.end(),
        std::back_inserter(positives), isPositive);
    printVec("正數:", positives);

    // 使用函式物件篩選特定範圍
    std::vector<int> inRange;
    std::copy_if(rawData.begin(), rawData.end(),
        std::back_inserter(inRange), InRange(0, 10));
    printVec("0-10 範圍:", inRange);

    // 使用預定義函式物件排序
    std::sort(positives.begin(), positives.end(), std::greater<int>());
    printVec("正數降序:", positives);

    // 使用預定義函式物件累加
    int positiveSum = std::accumulate(positives.begin(), positives.end(),
        0, std::plus<int>());
    std::cout << "正數總和:" << positiveSum << "\n";

    return 0;
}

관련 글