C++/functional
functional是b:C++標準程式庫中的一個b:頭文件,定義了C++ STL標準中的基礎性的算法(均為函數模板)。
命名空間placeholders
編輯定義了用作 std::bind 表達式中的未綁定實參的占位符常量:_1, _2, _3, _4, ...
類
編輯function類模板
編輯包裝具有指定的函數調用簽名的可調用對象類型。
必要性:如果lambda表達式賦值給一個函數指針,則編譯報錯。這就需要一個能保存各種可調用類型對象的辦法。實際上,lambda表達式有可能捕獲外部對象及其值做成閉包,因此每個lambda表達式都是獨一無二的類型。
std::function是一個可變參類模板,是一個通用的函數包裝器(Polymorphic function wrapper)。std::function的實例可以存儲、複製和調用任何可複製構造的可調用目標,包括普通函數、成員函數、類對象(重載了operator()的類的對象)、Lambda表達式等。是對C++現有的可調用實體的一種類型安全的包裹(相比而言,函數指針這種可調用實體,是類型不安全的)。
std::function中存儲的可調用對象被稱之為std::function的目標。若std::function中不含目標,調用不含目標的std::function會拋出std::bad_function_call 異常。
#include <functional>
#include <iostream>
// 定义一个类,用function包裹其成员
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_+i << '\n'; }
int num_;
};
// 定义一个普通函数,用function进行包裹
void print_num(int i)
{
std::cout << i << '\n';
}
// 定义一个重载了operator()的类,function可以包裹其对象
struct PrintNum {
void operator()(int i) const {
std::cout << i << '\n';
}
};
int main()
{
// 存储自由函数
std::function<void(int)> f_display = print_num;
f_display(-9);
// 存储 lambda
std::function<void()> f_display_42 = []() { print_num(42); };
f_display_42();
// 存储到 std::bind 调用的结果
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
f_display_31337();
// 存储到成员函数的调用,第一个入参为this指针
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
const Foo foo(314159);
// 第一个入参为this指针
f_add_display(foo, 1);
f_add_display(314159, 1);
// 存储到数据成员访问器的调用
std::function<int(Foo const&)> f_num = &Foo::num_;
std::cout << "num_: " << f_num(foo) << '\n';
// 存储到成员函数及对象的调用
using std::placeholders::_1;
std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
f_add_display2(2);
// 存储到成员函数和对象指针的调用
std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
f_add_display3(3);
// 存储到函数对象的调用
std::function<void(int)> f_display_obj = PrintNum();
f_display_obj(18);
auto factorial = [](int n) {
// 存储 lambda 对象以模拟“递归 lambda ”,注意额外开销
std::function<int(int)> fac = [&](int n){ return (n < 2) ? 1 : n*fac(n-1); };
// note that "auto fac = [&](int n){...};" does not work in recursive calls
return fac(n);
};
for (int i{5}; i != 8; ++i) { std::cout << i << "! = " << factorial(i) << "; "; }
}
mem_fn函數模板
編輯從成員指針創建出函數對象。對數據成員指針也適用。與bind函數模板的區別是,mem_fn只關注、包裝了類成員指針,而bind需要指明參數。例如:
#include <functional>
#include <iostream>
struct Foo {
void display_greeting() {
std::cout << "Hello, world.\n";
}
void display_number(int i) {
std::cout << "number: " << i << '\n';
}
int data = 7;
};
int main() {
Foo f;
auto greet = std::mem_fn(&Foo::display_greeting);
greet(&f);
auto print_num = std::mem_fn(&Foo::display_number);
print_num(&f, 42);
auto access_data = std::mem_fn(&Foo::data);
std::cout << "data: " << access_data(&f) << '\n';
}
bad_function_call類
編輯調用空的 std::function 時拋出的異常
is_bind_expression類模板
編輯若類型T是調用 std::bind 產生的類型,則此模板從 std::true_type 導出。對於任何其他類型,此模板從 std::false_type 導出。
is_placeholder類模板
編輯若類型T是標準占位符_1 、 _2 、 _3、……的類型,則此模板分別派生自std::integral_constant<int,1> 、 std::integral_constant<int,2> 、 std::integral_constant<int,3> 等。
若類型T不是標準占位符類型,則此模板派生自std::integral_constant<int,0>。
實際上,bind函數模板用is_placeholder來確定是第幾個參數的占位符。
reference_wrapper類模板
編輯可複製構造 (CopyConstructible) 且可複製賦值 (CopyAssignable) 的引用包裝器(引用的容器)。實現時,有一個數據類型的指針成員變量,在需要數據類型引用時返回相應的解引用。
hash類模板
編輯template< class Key > struct hash;
允許的特化模板類是「函數對象」,實現了哈希函數。即定義了operator() const
,接受一個Key類型的參數,返回size_t
的哈希結果值。對於一個類型,相同值具有相同的哈希結果,不同值具有不同的哈希結果(受限於size_t
的值域)。該類別用於4種無序關聯容器,不適用於加密算法。
各種基礎類型、大多數標準庫的類型(如std::string<、code>)已经特化实现了hash类模板。但对于
std::pair
,需要用boost::hash
對自定義的類實現hash功能,有兩種辦法:
#include <cstddef>
#include <functional>
#include <iomanip>
#include <iostream>
#include <string>
#include <unordered_set>
struct S
{
std::string first_name;
std::string last_name;
bool operator==(const S&) const = default; // since C++20
};
// Before C++20.
// bool operator==(const S& lhs, const S& rhs)
// {
// return lhs.first_name == rhs.first_name && lhs.last_name == rhs.last_name;
// }
// Custom hash can be a standalone function object.
struct MyHash
{
std::size_t operator()(const S& s) const noexcept
{
std::size_t h1 = std::hash<std::string>{}(s.first_name);
std::size_t h2 = std::hash<std::string>{}(s.last_name);
return h1 ^ (h2 << 1); // or use boost::hash_combine
}
};
// Custom specialization of std::hash can be injected in namespace std.
template<>
struct std::hash<S>
{
std::size_t operator()(const S& s) const noexcept
{
std::size_t h1 = std::hash<std::string>{}(s.first_name);
std::size_t h2 = std::hash<std::string>{}(s.last_name);
return h1 ^ (h2 << 1);
// or use boost::hash_combine:
// std::size_t seed = 0;
// boost::hash_combine(seed, s.first_name);
// boost::hash_combine(seed, s.last_name);
}
};
//推荐用法
template <class T> class A {
T x;
public:
A(T x) : x(x) {}
bool operator==(A const& b) { return x == b.x; } //可定义为成员或普通函数
//std::size_t hash_value() { return boost::hash<T>()(x); } //error不能定义为类成员函数
friend std::size_t hash_value(const A<T>& a) { return boost::hash<T>{}(a.x); }
};
//备选用法-编译器若不支持模板friend(不支持 ADL 的编译器)
// (不能声明为friend hash_value)hash_value需要在boost命名空间中定义
template <class T> class A1 {
T x;
public:
A1(T x) : x(x) {}
bool operator==(A1 const& b) { return x == b.x; }//可定义为成员或普通函数
std::size_t hash() const {return boost::hash<T>{}(x); }
};
template <class T>std::size_t hash_value(A1<T> x){return x.hash();}
int main()
{
std::string str = "Meet the new boss...";
std::size_t str_hash = std::hash<std::string>{}(str);
std::cout << "hash(" << std::quoted(str) << ") =\t" << str_hash << '\n';
S obj = {"Hubert", "Farnsworth"};
// Using the standalone function object.
std::cout << "hash(" << std::quoted(obj.first_name) << ", "
<< std::quoted(obj.last_name) << ") =\t"
<< MyHash{}(obj) << " (using MyHash) or\n\t\t\t\t"
<< std::hash<S>{}(obj) << " (using injected specialization)\n";
// Custom hash makes it possible to use custom types in unordered containers.
// The example will use the injected std::hash<S> specialization above,
// to use MyHash instead, pass it as a second template argument.
std::unordered_set<S> names = {obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"}};
for (auto const& s: names)
std::cout << std::quoted(s.first_name) << ' '
<< std::quoted(s.last_name) << '\n';
}
函數
編輯
bind函數模板
編輯
綁定一或多個實參到函數對象。參見示例一節。
ref與cref函數模板
編輯
創建具有從其實參推導的類型的 std::reference_wrapper
invoke函數模板
編輯
(C++17)以給定實參調用任意可調用 (Callable) 對象
函數對象
編輯
算術運算
編輯
- plus類模板
- minus類模板
- multiplies類模板
- divides類模板
- modulus類模板
- negate類模板
比較
編輯
- equal_to類模板
- not_equal_to類模板
- greater類模板
- less類模板
- greater_equal類模板
- less_equal類模板
C++20概念制約的比較
編輯
- ranges::equal_to類
- ranges::not_equal_to類
- ranges::greater類
- ranges::less類
- ranges::greater_equal類
- ranges::less_equal類
邏輯運算
編輯
- logical_and類模板
- logical_or類模板
- logical_not類模板
位運算
編輯
- bit_and類模板
- bit_or類模板
- bit_xor類模板
- bit_not類模板
取反器
編輯
not_fn函數模板:(C++17)創建返回其保有的函數對象的結果之補的函數對象
搜索器
編輯
- default_searcher類模板:(C++17)標準 C++ 庫搜索算法實現
- boyer_moore_searcher類模板:(C++17)Boyer-Moore 搜索算法實現
- boyer_moore_horspool_searcher類模板:(C++17)Boyer-Moore-Horspool 搜索算法實現
示例
編輯
#include <functional>
#include <iostream>
using namespace std;
std::function< int(int)> Functional;
// 普通函数
int TestFunc(int a)
{
return a;
}
// Lambda表达式
auto lambda = [](int a)->int { return a; };
// 仿函数(functor)
class Functor
{
public:
int operator()(int a)
{
return a;
}
};
// 1.类成员函数
// 2.类静态函数
class TestClass
{
public:
int ClassMember(int a) { return a; }
static int StaticMember(int a) { return a; }
};
int main()
{
// 普通函数
Functional = TestFunc;
int result = Functional(10);
cout << "普通函数:" << result << endl;
// Lambda表达式
Functional = lambda;
result = Functional(20);
cout << "Lambda表达式:" << result << endl;
// 仿函数
Functor testFunctor;
Functional = testFunctor;
result = Functional(30);
cout << "仿函数:" << result << endl;
// 类成员函数
TestClass testObj;
Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);
result = Functional(40);
cout << "类成员函数:" << result << endl;
// 类静态函数
Functional = TestClass::StaticMember;
result = Functional(50);
cout << "类静态函数:" << result << endl;
return 0;
}