最近更新于 2024-05-05 14:18
环境
Debian 11(arm64)
编译器 g++ 10.2.1;编译标准 C++20;参数:-std=c++20 -no-pie -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Werror=format-security -Wextra -pedantic -Wimplicit-fallthrough -Wsequence-point -Wswitch-unreachable -Wswitch-enum -Wstringop-truncation -Wbool-compare -Wtautological-compare -Wfloat-equal -Wshadow=global -Wpointer-arith -Wpointer-compare -Wcast-align -Wcast-qual -Wwrite-strings -Wdangling-else -Wlogical-op -Wconversion -g -O0
仿函数
在前面面向对象基础部分也提过仿函数(https://blog.iyatt.com/?p=9028 ),仿函数实质上就是对 () 小括号进行重载,也被称为函数对象
基础使用
#include <iostream>
class Add
{
private:
int m_num1, m_num2;
public:
int m_result;
int flag; // true 加数从仿函数传入;false 加数通过构造函数传入
// 默认构造
Add()
{
flag = true;
}
// 构造执行初始化
Add(int num1, int num2) : m_num1(num1), m_num2(num2)
{
flag = false;
}
// 重载 (),即仿函数
int operator()(int num1 = 0, int num2 = 0)
{
if (flag)
{
m_num1 = num1;
m_num2 = num2;
}
m_result = m_num1 + m_num2;
return m_result;
}
};
void test1(int num1, int num2, Add add)
{
std::cout << add(num1, num2) << std::endl;
}
void test2(Add add)
{
std::cout << add() << std::endl;
}
int main()
{
Add add1; // 实例化 1
int result = add1(9, 1); // 执行仿函数,可以通过返回值获取结果
std::cout << result << std::endl;
Add add2(1, 9); // 实例化 2
add2(); // 执行仿函数
std::cout << add2.m_result << std::endl; // 通过成员变量获取结果
// 匿名对象的应用,不实例化创建名字,直接使用类名创建一个临时对象
// 第一対括号创建匿名对象,调用构造函数初始化,第二对括号调用仿函数执行
std::cout << Add(15, 5)() << std::endl;
// 第一对括号时调用的默认构造函数,第二对括号调用仿函数并传入参数
std::cout << Add()(15, 5) << std::endl;
// 函数对象的应用
// Add 就是函数对象
test1(15, 15, Add());
test2(Add(15, 15));
}
谓词
返回值为 bool 的仿函数
#include <iostream>
#include <vector>
class Filter
{
public:
// 接受一个参数,一元谓词
bool operator()(int num)
{
if (num > 5)
{
return true;
}
return false;
}
};
int main()
{
std::vector<long> v1;
for (int i = 0; i < 10; ++i)
{
v1.push_back(random() % 10);
}
for (long i : v1)
{
std::cout << i << " ";
}
std::cout << std::endl;
// 寻找第一个大于 5 的数
// 一元谓词使用
std::vector<long>::const_iterator it = std::find_if(v1.begin(), v1.end(), Filter());
if (it != v1.end())
{
std::cout << *it << std::endl;
}
}
#include <iostream>
#include <vector>
#include <algorithm>
class Reverse
{
public:
bool operator()(int num1, int num2) // 二元谓词
{
if (num1 > num2)
{
return true;
}
return false;
}
};
void print(const std::vector<long> v)
{
for (long i : v)
{
std::cout << i << " ";
}
std::cout << std::endl;
}
int main()
{
std::vector<long> v1;
for (int i = 0; i < 10; ++i)
{
v1.push_back(random() % 10);
}
print(v1);
std::sort(v1.begin(), v1.end()); // 排序,默认升序
print(v1);
std::sort(v1.begin(), v1.end(), Reverse()); // 降序排序 - 二元谓词使用
print(v1);
}
内建仿函数
C++ 内部已经定义好了一些仿函数,使用需要包含头文件 functional
算数
算数运算
#include <iostream>
#include <functional>
int main()
{
std::plus<std::string> plus; // 加
std::string s1, s2;
s1 = "abc";
s2 = "123";
std::cout << plus(s1, s2) << std::endl;
std::cout << std::minus<int>()(10, 6) << std::endl; // 减
std::cout << std::multiplies<double>()(4.5, 2) << std::endl; // 乘
std::cout << std::divides<int>()(10, 5) << std::endl; // 除
std::cout << std::negate<int>()(43) << std::endl; // 相反数
std::cout << std::modulus<int>()(10, 4) << std::endl; // 取模
}
关系
比较运算
#include <iostream>
#include <functional>
int main()
{
std::cout << std::equal_to<std::string>()("abc", "123") << std::endl; // 等于
std::cout << std::not_equal_to<std::string>()("abc", "123") << std::endl; // 不等于
std::cout << std::less<int>()(10, 20) << std::endl; // 小于
std::cout << std::less_equal<int>()(10, 10) << std::endl; // 小于等于
std::cout << std::greater<int>()(10, 20) << std::endl; // 大于
std::cout << std::greater_equal<int>()(10, 20) << std::endl; // 大于等于
}
逻辑
逻辑运算
#include <iostream>
#include <functional>
int main()
{
// 逻辑与
std::cout << std::logical_and<bool>()(true, true) << std::endl;
std::cout << std::logical_and<bool>()(true, false) << std::endl;
std::cout << std::logical_and<bool>()(false, false) << std::endl;
// 逻辑或
std::cout << std::logical_or<bool>()(true, true) << std::endl;
std::cout << std::logical_or<bool>()(true, false) << std::endl;
std::cout << std::logical_or<bool>()(false, false) << std::endl;
// 逻辑非
std::cout << std::logical_not<bool>()(true) << std::endl;
std::cout << std::logical_not<bool>()(false) << std::endl;
}
位
位运算
#include <iostream>
#include <functional>
int main()
{
// 10 二进制是 10010,8 二进制是是 10000
std::cout << std::bit_and<int>()(10, 8) << std::endl; // 按位与
std::cout << std::bit_or<int>()(10, 8) << std::endl; // 按位或
std::cout << std::bit_xor<int>()(10, 8) << std::endl; // 按位异或
std::cout << std::bit_not<unsigned>()(10) << std::endl; // 按位取反
}
哈希
用于将数据类型转换为哈希值,用于支持哈希表等数据结构
#include <iostream>
#include <functional>
int main()
{
std::cout << std::hash<const char *>()("Hello world") << std::endl;
std::hash<const char *> hash;
std::cout << hash("hello world") << std::endl;
}
函数适配器
用于对已有的仿函数进行修改,使其满足特定的需求
// 绑定函数和一部分参数
#include <functional>
#include <iostream>
void f(std::string s1, std::string s2, std::string s3)
{
std::cout << s1 << " " << s2 << " " << s3 << std::endl;
}
int main()
{
// f 需要传入 3 个参数,这里适配成两个参数已经指定,只需传入一个参数的 ff
auto ff = std::bind(f, "hello", std::placeholders::_1, "world");
ff("你好");
// std::placeholders 是占位符,必须从 _1 开始编号,用于适配后的函数传入参数
auto fff = std::bind(f, "123", std::placeholders::_1, std::placeholders::_2);
fff("你好", "世界");
}
// 函数封装(普通函数、成员函数和 Lambda 表达式)
#include <iostream>
#include <functional>
void fun1()
{
std::cout << "fun1" << std::endl;
}
int fun2(int num1, int num2)
{
return num1 * num2;
}
class Fun3
{
public:
int fun(int num1, int num2)
{
return num1 + num2;
}
};
int main()
{
// 普通函数
std::function<void()> f1 = fun1;
f1();
std::function<int(int, int)> f2 = fun2;
std::cout << f2(3, 4) << std::endl;
// 成员函数
Fun3 fun3;
std::function<int(Fun3&, int, int)> f3 = &Fun3::fun;
std::cout << f3(fun3, 7, 3) << std::endl;
// Lambda 表达式
std::function<void()> f4 = [](){std::cout << "Lambda" << std::endl;};
f4();
}
补充:Lambda 表达式可参考 https://blog.iyatt.com/?p=9503