奇怪的东西

奇怪的东西

Lambda表达式

  其实就是C/C++函数的另一种定义方式

1
2
3
4
5
6
7
8
9
10
11
12
// Lambda
function<void(int)> dfs = [&](int u)
{
if(tree[u].l) dfs(tree[u].l);
if(tree[u].r) dfs(tree[u].r);

for(int i = 1; i <= m; ++i)
{
dp[u][i] = (dp[tree[u].l][i - 1] * dp[tree[u].r][i]) % mod;
dp[u][i] = (dp[u][i] + dp[u][i - 1]) % mod;
}
};
1
2
3
4
5
6
7
8
9
10
11
void dfs(int u)
{
if (tree[u].l) dfs(tree[u].l);
if (tree[u].r) dfs(tree[u].r);

for (int i = 1; i <= m; ++i)
{
dp[u][i] = (dp[tree[u].l][i - 1] * dp[tree[u].r][i]) % mod;
dp[u][i] = (dp[u][i] + dp[u][i - 1]) % mod;
}
}
  • function<void(int)>:一个 C++ 标准库中的 std::function 类型,表示一个可以接受一个 int 参数并返回 void 的可调用对象。
  • [&] :捕获列表.表示 lambda 表达式可以通过引用捕获周围作用域中的所有变量。使得你可以在 lambda内部访问外部的变量.
1
2
3
4
5
6
7
8
9
10
11
12
13
// 其他写法 
auto dfs = [&](this auto&& self, int u) -> void {
if(tree[u].l) self(tree[u].l);
if(tree[u].r) self(tree[u].r);

for(int i = 1; i <= m; ++i) {
dp[u][i] = (dp[tree[u].l][i - 1] * dp[tree[u].r][i]) % mod;
dp[u][i] = (dp[u][i] + dp[u][i - 1]) % mod;
}
};

// 调用方式
dfs(root_node); // 不再需要传递自身,c++23 或更高

stringstream

  stringstream 是 C++ 标准库中位于 <sstream> 头文件内的一个类,主要用作字符串的流式处理。它可以像流一样操作字符串,用来将不同类型的数据插入到字符串中或从字符串中提取数据,方便类型转换和字符串拼接。(豪丸)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//主要用法
//初始化
string str;
stringstream ss(str);

// >> <<
string input = "123 3.14 hello";
stringstream ss(input);
int num;
double pi;
string text;

string s;
getline(cin, s);
ss << s;
while(ss >> s) do something;

ss >> num >> pi >> text;

//清空
ss.str("")
ss.clear();

bitset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//初始化
std::bitset<8> b1; // 默认初始化为 00000000
std::bitset<8> b2(42); // 使用十进制数初始化为 00101010
std::bitset<8> b3("10101010"); // 使用二进制字符串初始化

//支持的操作
b1.set(3); // 将第三位设置为 1
b1.set(); // 将所有位设置为 1
b1.reset(); // 将所有位设置为 0
b1.reset(2); // 将第二位设置为 0
b1.flip(); // 反转所有位
b1.flip(1); // 反转第二位

std::cout << b2.count(); // 统计为 1 的位的个数
std::cout << b2.any(); // 如果至少有一位为 1,则返回 true
std::cout << b2.none(); // 如果所有位都为 0,则返回 true
std::cout << b2.size(); // 返回 bitset 的位数

std::cout << b2.to_string() << std::endl; // 输出:00101010
std::cout << b2.to_ulong() << std::endl; // 输出:42

类型转换

  static_cast,dynamic_cast,const_cast,reinterpret_cast

  C++ 中用于进行显式的类型转换的关键字,每个操作符有不同的用途和适用场景。比 C 风格的强制转换更具类型安全性,可读性。

static_cast

  用于普通的类型转换,适用于已知类型之间的转换。

1
2
double d = 3.14;
int i = static_cast<int>(d); // 转换 double 为 int

dynamic_cast

  用于处理类层次结构中的类型转换

1
2
3
4
5
6
7
8
9
10
11
12
class Base {
public:
virtual void foo() {} // 必须有虚函数以启用 RTTI
};

class Derived : public Base {};

Base* base = new Derived;
Derived* derived = dynamic_cast<Derived*>(base); // 成功转换
if (derived) {
std::cout << "Conversion successful" << std::endl;
}

const_cast

  用于去除或添加 const 限定符。

1
2
3
const int x = 10;
int* ptr = const_cast<int*>(&x); // 去除 const
*ptr = 20; // 如果在实际代码中修改了 const 对象,会导致未定义行为

reinterpret_cast

  用于进行低级别的指针或引用转换,允许你在不进行任何检查的情况下将一种类型转换为另一种完全不相关的类型。这种转换是最强大和最危险的。

1
2
int a = 10;
char* p = reinterpret_cast<char*>(&a); // 将 int 指针转换为 char 指针

奇怪的返回值类型

.size()

  STL的容器的.size()函数的返回值类型是size_t,他是一个无符号整数,位数却决于你的机器架构。intsize_t进行运算的时候,默认是将int转为size_t,这可能引起问题,需要我们自己进行类型转换。

it - numbers.begin()

  两个迭代器的差,返回值类型是difference_type,通常被定义为 ptrdiff_t 类型,是一个有符号整数。ptrdiff_t用于表示指针之间的差值,在 C++ 中常用于表示迭代器之间的差值,ptrdiff_t 的大小与机器架构相关。

emplace

  emplacepush_backinsert的上位替代,使用 emplace 时,直接在容器内部构造对象,避免了额外的拷贝或移动操作,而使用 insertpush_back时,通常需要先创建一个临时对象,然后再将其插入容器。

  emplace的返回值是std::pair,其中first是一个迭代器,指向插入的元素,secondbool,表示插入是否成功(对于自动去重的容器,元素已存在,则插入失败)。

  因为上面说的特性,存在如下情况。

1
2
3
4
set<pair<int, int>> st;
st.insert({1, 1}); //不报错,自己构造出了pair
st.emplace({1, 1}); //报错,emplace的参数应该是pair<int, int> 的构造函数参数
st.emplace(1, 1); //不报错

函数对象

  函数对象(也称为 仿函数,Functor)是 C++ 中行为像函数的对象。它通过重载 operator() 来实现函数调用,使得对象可以像函数一样被调用。

1
2
3
4
5
6
7
8
9
10
11
struct Adder {
int operator()(int a, int b) const {
return a + b;
}
};

int main() {
Adder add; // 创建函数对象
int result = add(3, 5); // 调用 operator(),返回 8
cout << result << endl;
}

  hash<int>{} 是 C++ 标准库提供的哈希函数对象,用于计算 int 类型的哈希值。它是 std::hash 模板的一个特化版本。

1
2
3
4
5
6
7
8
#include <functional> //与函数对象(function objects)、函数包装器、绑定器和操作符封装相关的工具。
#include <iostream>

int main() {
std::hash<int> hasher; // 创建哈希函数对象
size_t hash_value = hasher(42); // 计算 42 的哈希值
std::cout << hash_value << std::endl;
}

  Lambda 表达式的底层也是函数对象。

  顺便讲一下这个位置的const的作用,有两个:

  1. 保证这个函数不会修改成员变量
  2. 觉得对象能否调用这个成员函数,具体如下:
    • const 成员函数const 对象和非 const 对象都能调用。
    • const 成员函数:只有非 const 对象都能调用。

  一个应用:自定义哈希函数(一些奇怪的数据类型没有默认哈希函数,作为map的键时会报错,此时需要自定义)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <unordered_map>
#include <functional>
#include <string>

struct Person {
std::string name;
int age;
};

struct PersonHash {
std::size_t operator()(const Person& p) const {
return std::hash<std::string>{}(p.name) ^ std::hash<int>{}(p.age);
}
};

int main() {
std::unordered_map<Person, int, PersonHash> person_map;
person_map[{"Alice", 30}] = 100;
}