C++ 標準庫在 <functional>
頭文件中為我們提供了一套非常方便的預定義函數對象(也稱為“仿函數”或 “functor”),它們可以像變量一樣直接傳遞給 std::reduce
和其他標準算法。
你提到的 std::bit_or
和 std::multiplies
就是其中的成員。這些函數對象的好處是代碼更具可讀性,并且可以避免手寫簡單的 lambda 表達式。
從 C++14 開始,這些函數對象大多有了“透明”版本(使用 std::plus<>
而不是 std::plus<int>
),這讓它們使用起來更加方便,因為你不需要手動指定類型,編譯器會自動推斷。
下面是這些常用函數對象的分類介紹和 std::reduce
的使用示例。
1. 算術操作 (Arithmetic Functors)
這些是最常見的聚合操作。
函數對象 (透明版本) | 作用 | C++ 操作符 | 示例 reduce 初始值 |
---|---|---|---|
std::plus<>() | 加法 | a + b | 0 或 0.0 |
std::minus<>() | 減法 | a - b | N/A (不滿足結合律) |
std::multiplies<>() | 乘法 | a * b | 1 或 1.0 |
std::divides<>() | 除法 | a / b | N/A (不滿足結合律) |
std::modulus<>() | 取模 | a % b | N/A (不滿足結合律) |
std::negate<>() | 取反 (一元) | -a | (不適用于reduce ) |
注意: minus
和 divides
通常不用于 std::reduce
,因為它們不滿足并行計算所必需的結合律。(((a-b)-c)-d)
的結果與 (a-b) + (c-d)
通常是不同的。
代碼示例:
#include <iostream>
#include <vector>
#include <numeric>
#include <functional> // 必須包含int main() {std::vector<int> nums = {1, 2, 3, 4, 5};// 求和int sum = std::reduce(nums.begin(), nums.end(), 0, std::plus<>());std::cout << "Sum: " << sum << std::endl; // 輸出: 15// 求積long long product = std::reduce(nums.begin(), nums.end(), 1LL, std::multiplies<>());std::cout << "Product: " << product << std::endl; // 輸出: 120
}
2. 位運算 (Bitwise Functors)
這些在你需要對一系列整數進行位操作時非常有用。
函數對象 (透明版本) | 作用 | C++ 操作符 | 示例 reduce 初始值 |
---|---|---|---|
std::bit_and<>() | 按位與 | a & b | ~0 (所有位都為1) |
std::bit_or<>() | 按位或 | `a | b` |
std::bit_xor<>() | 按位異或 | a ^ b | 0 |
std::bit_not<>() | 按位取反 (一元) | ~a | (不適用于reduce ) |
代碼示例:
#include <iostream>
#include <vector>
#include <numeric>
#include <functional>int main() {std::vector<unsigned int> flags = {0b0001, 0b0010, 0b1000}; // 1, 2, 8// 將所有標志位合并 (OR)// 0 | 1 | 2 | 8 = 11 (0b1011)unsigned int all_flags = std::reduce(flags.begin(), flags.end(), 0u, std::bit_or<>());std::cout << "All flags (OR): " << all_flags << std::endl; // 輸出: 11// 找到所有共有位 (AND)std::vector<unsigned int> masks = {0b1101, 0b0111, 0b1111};// 0b1101 & 0b0111 & 0b1111 = 0b0101 (5)// 初始值需要是全1,否則任何數與0做&運算都會得到0unsigned int common_bits = std::reduce(masks.begin(), masks.end(), ~0u, std::bit_and<>());std::cout << "Common bits (AND): " << common_bits << std::endl; // 輸出: 5
}
3. 邏輯運算 (Logical Functors)
這些通常用于聚合布爾值。
函數對象 (透明版本) | 作用 | C++ 操作符 | 示例 reduce 初始值 |
---|---|---|---|
std::logical_and<>() | 邏輯與 | a && b | true |
std::logical_or<>() | 邏輯或 | `a | |
std::logical_not<>() | 邏輯非 (一元) | !a | (不適用于reduce ) |
代碼示例:
#include <iostream>
#include <vector>
#include <numeric>
#include <functional>int main() {std::vector<bool> conditions = {true, false, true};// 檢查是否所有條件都為真 (AND)bool all_true = std::reduce(conditions.begin(), conditions.end(), true, std::logical_and<>());std::cout << "All true? " << std::boolalpha << all_true << std::endl; // 輸出: false// 檢查是否至少一個條件為真 (OR)bool any_true = std::reduce(conditions.begin(), conditions.end(), false, std::logical_or<>());std::cout << "Any true? " << std::boolalpha << any_true << std::endl; // 輸出: true
}
4. 比較運算 (Comparison Functors) - 用于求最值
比較運算符本身不直接用于聚合,但它們是構建求最大/最小值邏輯的核心。雖然你可以直接使用 lambda 表達式 std::min
或 std::max
,但了解它們的存在也很有用。
函數對象 (透明版本) | 作用 | C++ 操作符 |
---|---|---|
std::equal_to<>() | 等于 | a == b |
std::not_equal_to<>() | 不等于 | a != b |
std::greater<>() | 大于 | a > b |
std::less<>() | 小于 | a < b |
std::greater_equal<>() | 大于等于 | a >= b |
std::less_equal<>() | 小于等于 | a <= b |
求最值的最佳實踐是使用 Lambda 表達式,因為它們更清晰。
代碼示例:
#include <iostream>
#include <vector>
#include <numeric>
#include <algorithm> // for std::min/maxint main() {std::vector<int> nums = {10, -5, 100, 30, -20};if (nums.empty()) return 1;// 求最大值// 初始值設為第一個元素,避免空容器或所有元素都為負數的問題int max_val = std::reduce(std::next(nums.begin()), // 從第二個元素開始nums.end(), // 到末尾nums.front(), // 初始值為第一個元素[](int a, int b) { return std::max(a, b); } // 使用 std::max);std::cout << "Max value: " << max_val << std::endl; // 輸出: 100// 求最小值int min_val = std::reduce(std::next(nums.begin()),nums.end(),nums.front(),[](int a, int b) { return std::min(a, b); } // 使用 std::min);std::cout << "Min value: " << min_val << std::endl; // 輸出: -20
}
總結
<functional>
頭文件是你的好朋友,它提供了豐富的預定義函數對象。- 使用透明函數對象(如
std::plus<>()
)是現代C++的最佳實踐,代碼更簡潔。 - 對于簡單的、標準的操作(加、乘、位運算),直接使用這些函數對象非常方便。
- 對于更復雜的邏輯,尤其是求最值,Lambda 表達式通常是更靈活、更具可讀性的選擇。
- 使用
reduce
時,選擇正確的初始值至關重要,它決定了整個計算的基礎。