记录一些刷题、学习过程中接触到的一些库函数,后续(应该会)持续更新。

# i、std::function

#CPP 新特性 #CPP11
std::function 是 C++11 标准引入的一个功能强大的模板类,它用于包装和管理可调用对象,包括函数指针、函数对象、Lambda 表达式和成员函数指针等。 std::function 允许你以统一的方式存储和调用不同类型的可调用对象。

以下是 std::function 的一些重要特性和用法:

1、创建 std::function 对象:你可以使用 std::function 模板来创建一个对象,该对象可以存储可调用对象,如函数指针、Lambda 表达式或函数对象。

1
std::function<int(int, int)> add = [](int a, int b) { return a + b; };

在上面的示例中,我们创建了一个 std::function 对象 add ,它可以接受两个整数参数并返回它们的和。

2、调用 std::function 对象:你可以像调用函数一样使用 std::function 对象。

1
int result = add(3, 4); // 调用 add 函数

你可以将 std::function 对象当作函数使用,无论它包装的是哪种可调用对象。

3、类型安全std::function 提供了类型安全的方式来管理可调用对象。你可以在编译时检查是否使用了正确的参数类型。

4、空对象检查:你可以检查 std::function 对象是否包含可调用对象,以避免在未初始化的情况下调用它。

1
2
3
if(add) {
int result = add(3, 4);
}

5、多态性std::function 允许你在运行时切换不同的可调用对象,使代码更加灵活

6、存储成员函数指针:你可以存储成员函数指针,并将对象的指针作为第一个参数传递给成员函数。

1
2
3
4
5
6
7
struct MyClass {
int add(int a, int b) { return a + b; }
};

MyClass obj;
std::function<int(MyClass*, int, int)> memberFunction = &MyClass::add;
int result = memberFunction(&obj, 3, 4);

即绑定对象的 this 指针。相关内容可参考:成员函数指针与普通函数指针的差异

7、函数参数绑定:你可以使用 std::bind 或 Lambda 表达式将参数绑定到 std::function 对象上,以创建可重用的部分函数。

1
2
std::function<int(int)> addThree = std::bind(add, std::placeholders::_1, 3);
int result = addThree(4); // 结果为 7

std::function 是一种非常有用的工具,特别适用于需要动态调用不同函数或需要将函数作为参数传递的情况。它提供了更高级别的抽象,使得 C++ 中的函数对象和回调更加灵活和可维护。

# i、std::bind

std::bind 是 C++11 引入的函数对象(Function Object)库的一部分,用于创建函数对象(或者叫绑定对象),它可以用来将函数或成员函数与一组参数绑定在一起,创建一个新的可调用对象。 std::bind 提供了一种非常强大的机制,允许你灵活地构建新的函数对象,包括改变参数的顺序、固定某些参数的值,以及创建函数适配器。

std::bind  可以看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表

1
auto newCallable = bind(callable, arg_list);

该形式表达的意思是:当调用 newCallable 时,会调用 callable,并传给它  arg_list  中的参数。

预绑定的参数是以值传递的形式,不预绑定的参数是以引用传递的形式。

后者要用  std::placeholders  (占位符)的形式占位,从  _1  开始,依次递增。

1
2
3
4
if (!m_engine->GetComponent("DDSSimulator")) {
std::thread period_send_thread(std::bind(&DDSOutput::SendFusionPeriodThread, this));
period_send_thread.detach();
}

以下是 std::bind 的一些主要用法和特性:

1、绑定全局函数

1
2
3
4
5
6
7
8
9
10
11
12
#include <functional>
#include <iostream>

void greet(const std::string& name) {
std::cout << "Hello, " << name << "!" << std::endl;
}

int main() {
auto sayHello = std::bind(greet, "John");
sayHello(); // 输出: Hello, John!
return 0;
}

2、绑定成员函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <functional>
#include <iostream>

class Greeter {
public:
void greet(const std::string& name) {
std::cout << "Hello, " << name << "!" << std::endl;
}
};

int main() {
Greeter greeter;
auto greetFunction = std::bind(&Greeter::greet, &greeter, std::placeholders::_1);
greetFunction("Alice"); // 输出: Hello, Alice!
return 0;
}

3、参数绑定和重排序

1
2
3
4
5
6
7
8
9
10
11
12
#include <functional>
#include <iostream>

void printThreeArgs(int a, int b, int c) {
std::cout << a << ", " << b << ", " << c << std::endl;
}

int main() {
auto printArgs = std::bind(printThreeArgs, std::placeholders::_3, 100, std::placeholders::_1);
printArgs(1, 2, 3); // 输出: 3, 100, 1
return 0;
}

4、创建函数适配器std::bind 允许你创建函数适配器,用于改变函数的行为,比如取反或者应用一元或二元函数。

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

bool isEven(int num) {
return num % 2 == 0;
}

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
auto isOdd = std::not1(std::bind(isEven, std::placeholders::_1));
// 这里的 std::not1 用于将谓词函数的结果取反,即 true 变为 false,false 变为 true

for (int num : numbers) {
if (isOdd(num)) {
std::cout << num << " is odd." << std::endl;
}
} return 0;
}

5、绑定到引用参数std::bind 还可以用于将参数绑定到引用参数,以允许在调用时改变参数的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <functional>
#include <iostream>

void addOne(int& value) {
value += 1;
}

int main() {
int number = 42;
auto increment = std::bind(addOne, std::ref(number));
increment();
std::cout << "Number after increment: " << number << std::endl;
// 输出: Number after increment: 43
return 0;
}

注意,这里的 std::ref 的作用是创建对对象的引用。
从这个例子中我们可以更好地理解 bind 这个函数,它其实就是个包装器,浅封装了另一个可调用对象及其参数。那么,在封装这些参数时就可以指定他们是值绑定还是引用绑定。
默认情况下,bind 传递参数是值绑定,用  std::placeholders  占位符进行占位的参数是引用绑定,即我们前面所提到的:预绑定的参数是以值传递的形式,不预绑定的参数是以引用传递的形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void addOne(int& value) {
value += 1;
}

auto increment = std::bind(addOne, std::ref(number));
//等价于
void increment(int& val){
addOne(val);
}

//而如果我们去掉了 std::ref,则
auto increment = std::bind(addOne, number);
//相当于
void increment(int val){
addOne(val);
}

//也就是说,虽然我们底层调用的 addOne 函数是值传递的参数,但是其外部包装的函数入参可以进一步控制

std::bind_frontstd::bind_back 在 bind 的基础上绑定前几个参数或后几个参数:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <functional>

void foo(int a, int b, int c) {
std::cout << a << " " << b << " " << c << std::endl;
}

int main() {
auto bound = std::bind_front(foo, 42, 10);
bound(5); // 输出: 42 10 5
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <functional>

void bar(int a, int b, int c) {
std::cout << a << " " << b << " " << c << std::endl;
}

int main() {
auto bound = std::bind_back(bar, 5, 10);
bound(42); // 输出: 42 5 10
return 0;
}

# i、std::ref

std::ref 是一个标准库函数模板,用于创建一个可以传递引用的包装对象。这在需要将对象的引用传递给其他函数或线程时非常有用,尤其是在标准库函数不支持直接传递引用的情况下(例如, std::thread 构造函数期望的是传值语义)。

std::ref 创建一个 std::reference_wrapper 对象,这个对象持有对传入对象的引用,并且可以像普通引用一样使用。 std::reference_wrapper 可以通过 operator T&() 进行隐式转换,允许引用在需要值的地方被使用。

具体使用场景:

  1. 线程参数传递:当我们使用 std::thread 时,参数通常是按值传递的。如果需要传递引用,就可以使用 std::ref
  2. 函数调用:在标准库中,某些函数接受函数对象(如函数、lambda、或 std::function )时,若需要传递引用,可以使用 std::ref
  3. 容器中存储引用:STL 容器通常存储值,如果需要存储引用,可以使用 std::reference_wrapper

线程参数传递示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <thread>
#include <future>
#include <functional> // for std::ref

void addTwoValue(std::promise<int>& prom, int a, int b) {
std::this_thread::sleep_for(std::chrono::seconds(2));
int result = a + b;
prom.set_value(result);
}

int main() {
std::promise<int> prom;

// 使用 std::ref 来传递引用
std::thread task(addTwoValue, std::ref(prom), 5, 3);
std::cout << "waiting for thread to generate result" << std::endl;

std::future<int> fu = prom.get_future();
int result = fu.get();
std::cout << "getting result " << result << std::endl;

task.join();

return 0;
}

函数调用示例:

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

void printValue(int& x) {
std::cout << "Value: " << x << std::endl;
}

int main() {
int a = 10;

// 直接调用
printValue(a);

// 使用 std::ref 传递引用
std::function<void()> func = std::bind(printValue, std::ref(a));
func();

return 0;
}

容器中存储引用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <vector>
#include <functional>

int main() {
int a = 10;
int b = 20;

// 创建一个引用包装器的容器
std::vector<std::reference_wrapper<int>> ref_container;
ref_container.push_back(a);
ref_container.push_back(b);

// 修改原变量的值
a = 30;
b = 40;

// 通过引用包装器访问修改后的值
for (auto& ref : ref_container) {
std::cout << ref << std::endl;
}

return 0;
}

std::ref 在需要传递引用但接口不支持的场景下非常有用,特别是在多线程编程中传递引用参数时。通过使用 std::ref 可以避免不必要的拷贝,保持对原对象的引用,从而提高效率和灵活性。

# i、std::pair

std::pair 是 C++ 标准库中的一个模板类,用于将两个值组合在一起,形成一个有序对。 std::pair 提供了一种简单的方式来将两个不同类型的值打包,方便传递和返回多个值。 std::pair 的声明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
template <class T1, class T2>
struct pair {
T1 first;
T2 second;

// 构造函数
pair(const T1& x, const T2& y) : first(x), second(y) {}

// 默认构造函数
pair() : first(), second() {}

// 拷贝构造函数
template<class U1, class U2>
pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}

// 移动构造函数
template<class U1, class U2>
pair(pair<U1, U2>&& p) : first(std::forward<U1>(p.first)), second(std::forward<U2>(p.second)) {}

// 其他成员函数...
};

std::pair 的主要特点包括:

  • firstsecond 成员变量,用于存储两个值。
  • 构造函数,允许通过传递参数进行初始化。
  • 默认构造函数,创建一个默认构造的 pair 对象。
  • 拷贝构造函数,用于从其他 pair 对象进行拷贝构造。
  • 移动构造函数,用于从其他 pair 对象进行移动构造。

例如,使用 std::pair 来创建一个包含整数和浮点数的对象:

1
2
3
4
5
6
7
8
#include <iostream>
#include <utility>

int main() {
std::pair<int, double> myPair(42, 3.14);
std::cout << "Pair: " << myPair.first << ", " << myPair.second << std::endl;
return 0;
}

我们定义的 pair 模板类会有默认构造,其会调用两个成员变量的默认构造。

# i、std::make_pair

std::make_pair 是一个模板函数,用于创建 std::pair 对象。它接受两个参数,并根据这两个参数的类型自动推导出 pair 的模板参数类型。 std::make_pair 的声明如下:

1
2
3
template<class T1, class T2>
constexpr std::pair<typename std::decay<T1>::type, typename std::decay<T2>::type>
make_pair(T1&& t, T2&& u);

其中:

  • T1T2std::pair 的两个模板参数类型。
  • std::decay 是一个模板元函数,用于移除类型的引用和 cv 修饰符,确保类型是纯净的。
  • T1&& tT2&& ustd::make_pair 函数的参数,使用右值引用,允许传递左值或右值。

std::make_pair 的作用是根据传入的参数创建一个 std::pair 对象。通过使用 std::make_pair ,我们可以避免显式指定模板参数类型,使代码更简洁。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <utility>

int main() {
int x = 42;
double y = 3.14;

auto myPair = std::make_pair(x, y);

std::cout << "Pair: " << myPair.first << ", " << myPair.second << std::endl;

return 0;
}

这里, std::make_pair(x, y)xy 包装成一个 std::pair<int, double> 对象,并自动推导出 pair 的模板参数类型。

这里要注意的是,make_pair 在传入两个变量时,会调用传入参数的拷贝构造函数(当然,上面这个例子里面 int 和 double 这种基本类型就没这种说法了)。

1
2
3
4
//假设我们有个类型 A
A a1(5);
A a2(7);
std::make_pair(a1, a2); //这里会发生两次 A 的拷贝构造

核心思想:高效快速判断元素是否存在。

C++ STL(标准模板库)提供了一个二分查找算法,即 std::binary_search 函数。这个函数用于在已排序的序列中查找特定元素

函数签名如下:

1
2
template<class ForwardIt, class T>
bool binary_search(ForwardIt first, ForwardIt last, const T& value);

参数说明:

  • firstlast 是表示序列范围的迭代器,指定了要进行查找的区间。
  • value 是要查找的目标值。

std::binary_search 函数返回一个布尔值,表示是否找到了目标值。如果找到了,返回 true ;如果未找到,返回 false

这个函数使用二分查找算法,在已排序的序列中进行查找。它会在指定的范围内进行迭代,并根据当前位置的值与目标值的比较结果来决定继续查找的方向。

使用 std::binary_search 函数的前提是,序列必须是已排序的。如果序列未排序,结果是不可预测的。如果需要在未排序的序列中进行查找,可以先使用 std::sort 函数对序列进行排序,然后再使用 std::binary_search 进行查找。

下面是一个使用 std::binary_search 函数的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int target = 7;

// 使用 std::binary_search 查找目标值
bool found = std::binary_search(numbers.begin(), numbers.end(), target);

if (found) {
std::cout << "Target found in the sequence." << std::endl;
} else {
std::cout << "Target not found in the sequence." << std::endl;
}

return 0;
}

这个示例中,我们创建了一个已排序的整数序列,并使用 std::binary_search 函数查找目标值 7。如果找到了,输出 "Target found in the sequence.";如果未找到,输出 "Target not found in the sequence."。

请注意,对于重复的元素, std::binary_search 只能确定序列中是否存在目标值,而不能确定目标值的具体位置。如果需要确定目标值的位置,可以使用 std::lower_bound 函数或 std::upper_bound 函数。

尽管 std::binary_search 无法提供目标值的具体位置,但它在许多情况下可以提供一个高效的方法来检查存在性,以便根据需要采取进一步的操作。

# i、lower_bound && upper_bound

当我们在已排序的序列中进行查找时,除了使用 std::binary_search 函数之外,还可以使用 std::lower_boundstd::upper_bound 函数来获取目标值在序列中的位置。

std::lower_bound 函数用于在已排序的序列中找到第一个不小于目标值的元素的位置,函数签名如下:

1
2
template<class ForwardIt, class T>
ForwardIt lower_bound(ForwardIt first, ForwardIt last, const T& value);

参数说明:

  • firstlast 是表示序列范围的迭代器,指定了要进行查找的区间。
  • value 是要查找的目标值。

std::lower_bound 函数返回一个迭代器,指向序列中第一个不小于目标值的元素。如果序列中不存在不小于目标值的元素,返回的迭代器将指向序列的末尾。

std::upper_bound 函数用于在已排序的序列中找到第一个大于目标值的元素的位置,函数签名如下:

1
2
template<class ForwardIt, class T>
ForwardIt upper_bound(ForwardIt first, ForwardIt last, const T& value);

参数说明:

  • firstlast 是表示序列范围的迭代器,指定了要进行查找的区间。
  • value 是要查找的目标值。

std::upper_bound 函数返回一个迭代器,指向序列中第一个大于目标值的元素。如果序列中不存在大于目标值的元素,返回的迭代器将指向序列的末尾。

下面是一个使用 std::lower_boundstd::upper_bound 函数的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 5, 7, 8, 9, 10};
int target = 5;

// 使用 std::lower_bound 查找不小于目标值的第一个元素
auto lower = std::lower_bound(numbers.begin(), numbers.end(), target);

// 使用 std::upper_bound 查找大于目标值的第一个元素
auto upper = std::upper_bound(numbers.begin(), numbers.end(), target);

// 输出查找结果
std::cout << "Lower bound: " << std::distance(numbers.begin(), lower) << std::endl;
std::cout << "Upper bound: " << std::distance(numbers.begin(), upper) << std::endl;

return 0;
}

这个示例中,我们创建了一个已排序的整数序列,并使用 std::lower_bound 函数查找不小于目标值 5 的第一个元素,以及使用 std::upper_bound 函数查找大于目标值 5 的第一个元素。我们通过 std::distance 函数计算迭代器与序列起始位置的距离,从而得到目标值在序列中的位置。

请注意, std::lower_boundstd::upper_bound 函数的时间复杂度为 O (log N),其中 N 是序列中的元素数量。这两个函数在查找范围较大的有序序列时具有较高的效率。

1
2
Lower bound: 4
Upper bound: 6

upper_bound 并不能确定某个 target 是否存在数组中,而只能判断是否存在大于 target 的数字。

std::upper_bound 函数返回的是第一个大于目标值的元素的迭代器,而不是判断目标值是否存在于数组中。如果目标值存在于数组中,它将返回大于目标值的第一个元素的迭代器;如果目标值在数组中不存在,它将返回大于目标值的最小元素的迭代器,即序列的上界。

如果你想要确定目标值是否存在于数组中,可以将 std::upper_bound 返回的迭代器与目标值进行比较,如果它指向数组的起始位置或者它的前一个元素与目标值相等,则说明目标值存在于数组中。

# i、partial_sum

在 C++ 标准库中,可以使用 std::partial_sum 函数来求一个序列的前缀和(partial sum)。 std::partial_sum 函数是一个算法,它对输入范围内的元素进行部分求和,并将结果存储在输出序列中。

以下是 std::partial_sum 函数的原型:

1
2
template <class InputIterator, class OutputIterator, class BinaryOperation>
OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result, BinaryOperation binary_op);

其中, InputIteratorOutputIterator 是模板参数,它们分别表示输入序列和输出序列的迭代器类型。 BinaryOperation 是一个函数对象(或函数指针),用于指定计算前缀和的二元操作。 firstlast 分别表示输入序列的起始和结束位置(左闭右开区间)。 result 表示输出序列的起始位置。

std::partial_sum 函数的功能是将位于 [first, last) 范围内的元素从输入序列复制到输出序列,并依次计算部分和。它将结果存储在输出序列中,并返回指向输出序列中最后一个复制元素之后位置的迭代器。

默认情况下, std::partial_sum 使用加法 + 运算符进行前缀和计算。如果要使用其他的二元操作,可以通过传递自定义的函数对象或函数指针 binary_op 来实现。

以下是使用 std::partial_sum 函数求前缀和的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::vector<int> prefixSum(numbers.size());

// 使用 std::partial_sum 求前缀和,并直接写入到输出容器
std::partial_sum(numbers.begin(), numbers.end(), prefixSum.begin());

// 打印前缀和序列
for (const auto& num : prefixSum) {
std::cout << num << " ";
} std::cout << std::endl;

return 0;
}

在上面的示例中,我们通过 std::partial_sum 函数计算 numbers 中元素的前缀和,并将结果直接写入到 prefixSum 容器中。在这里,我们在调用 std::partial_sum 时,将 prefixSum.begin() 作为输出序列的起始位置,这样 partial_sum 会将计算得到的前缀和直接写入到 prefixSum 容器中。

# i、priority_queue

使用 priority_queue 需要包含头文件 <queue> ,默认情况下, priority_queue 会按照元素类型的比较函数(默认为 less ,即降序排列)来维护元素的优先级。如果需要按照其他方式来定义优先级(例如升序排列),可以使用自定义的比较函数。

其基本操作同 queue,即 push、top、pop,只是多了排序的特性。(但 pq 只保证顶部元素是有序的,不保证整个队列是有序的。因为它的本质是大小堆,只能保证当前节点比左右子节点大 / 小,而每一次进行 push 或者 pop 进行元素的增删时,都会使得节点进行交换位置,可能打乱顺序。)

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <queue>

int main() {
// 默认情况下,创建一个降序的优先级队列
std::priority_queue<int> pq;

// 插入元素
pq.push(10);
pq.push(30);
pq.push(20);

// 访问和删除元素
while (!pq.empty()) {
std::cout << pq.top() << " "; // 访问最大元素
pq.pop(); // 移除最大元素
}

std::cout << std::endl;

// 自定义比较函数,创建一个升序的优先级队列
std::priority_queue<int, std::vector<int>, std::greater<int>> pq_custom;

// 插入元素
pq_custom.push(50);
pq_custom.push(10);
pq_custom.push(30);

// 访问和删除元素
while (!pq_custom.empty()) {
std::cout << pq_custom.top() << " "; // 访问最小元素
pq_custom.pop(); // 移除最小元素
}

std::cout << std::endl;

return 0;
}

# i、accumulate

std::accumulate 是 C++ 标准库中的一个算法,用于对给定范围内的元素进行累积操作。它定义在头文件 <numeric> 中,接口定义如下:

1
2
template <class InputIterator, class T>
T accumulate (InputIterator first, InputIterator last, T init);

  • InputIterator first :表示范围的起始迭代器,指向要累积的元素的第一个元素。
  • InputIterator last :表示范围的结束迭代器,指向要累积的元素的最后一个元素的下一个位置(即不包含在范围内)。
  • T init :表示累积的初始值。

accumulate 函数会从 firstlast 对范围内的元素进行累积操作,将每个元素依次加到 init 上,然后返回最终的累积结果。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>
#include <numeric> // 包含 accumulate 函数

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};

// 使用 accumulate 对 numbers 中的元素进行累积操作,初始值为0
int sum = std::accumulate(numbers.begin(), numbers.end(), 0);

std::cout << "Sum of elements: " << sum << std::endl;

return 0;
}

# i、minmax_element

std::minmax_element 是 C++ 标准库中的一个算法函数,用于在给定范围内查找最小值和最大值的元素,并返回指向这两个元素的迭代器。

以下是 std::minmax_element 函数的原型:

1
2
template <class ForwardIterator>
std::pair<ForwardIterator, ForwardIterator> minmax_element(ForwardIterator first, ForwardIterator last);

ForwardIterator 是模板参数,它表示输入序列的迭代器类型。 firstlast 分别表示输入序列的起始和结束位置(左闭右开区间)。

std::minmax_element 函数的功能是在范围 [first, last) 内查找最小值和最大值的元素,并返回一个 std::pair 对象,其中 first 成员是指向最小值元素的迭代器, second 成员是指向最大值元素的迭代器。

以下是使用 std::minmax_element 函数查找最小值和最大值的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> numbers = {5, 2, 8, 3, 1, 9, 4, 7, 6};

// 使用 std::minmax_element 查找最小值和最大值
auto minmaxPair = std::minmax_element(numbers.begin(), numbers.end());

// 输出结果
std::cout << "最小值: " << *minmaxPair.first << std::endl;
std::cout << "最大值: " << *minmaxPair.second << std::endl;

return 0;
}

在上面的示例中,我们使用 std::minmax_element 函数在 numbers 中查找最小值和最大值,并将结果存储在 minmaxPair 中。然后通过 minmaxPair.firstminmaxPair.second 分别访问最小值和最大值的元素。

# i、sort

sort 虽然很容易,但是老是忘记一些细节:
sort 默认是从小到大排序,sort 第三个参数为可选参数,可用于传入比较函数。
STL 提供了两个内置的比较函数 less 和 greater,具体使用如下:

1
2
std::sort(nums.begin(), nums.end(), std::less<int>());    //从小到大
std::sort(nums.begin(), nums.end(), std::greater<int>()); //从大到小

当然,第三个参数也可以是自定义函数,比如传入 lambda 表达式。

# i、remove

std::remove 从指定范围内删除指定的值。

它是 C++ 标准库中的一个算法函数,用于从序列中删除指定的元素。它的主要作用是将需要删除的元素移到序列的末尾,然后返回一个指向新范围结尾之后位置的迭代器。它并不会真正地删除元素,而是通过返回新的范围来使得这些元素 “看起来” 被删除了。

其声明如下:

1
2
template <class ForwardIt, class T>
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);

参数说明:

  • firstlast :表示要操作的范围, [first, last) 为一个半开区间,包含要进行操作的元素
  • value :要删除的特定值。

返回值是一个迭代器,指向新范围结尾之后的位置。这个迭代器可以用来调整容器的大小,实现元素的 “删除”。

以下是一个使用 std::remove 的示例代码,移除容器中的特定元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 3, 5, 6};

// 使用 std::remove 将所有值为 3 的元素移到末尾
auto newEnd = std::remove(numbers.begin(), numbers.end(), 3);

// 调整容器大小,删除被移到末尾的元素
numbers.erase(newEnd, numbers.end());

// 输出删除元素后的容器
for (const int& num : numbers) {
std::cout << num << " ";
} return 0;
}

# i、erase

在 C++ 中, erase 是用于容器的成员函数,主要用于删除容器中的元素。它在不同类型的容器(如 std::vectorstd::liststd::map 等)中都有不同的用法,但其基本作用是从容器中删除一个或多个元素。以下是关于 erase 函数的一些常见用法:

  1. 删除单个元素:

    • 对于 std::vectorstd::list 等序列容器,可以使用迭代器来删除指定位置的元素。
      1
      2
      3
      std::vector<int> numbers = {1, 2, 3, 4, 5};
      auto it = numbers.begin() + 2; // 删除第三个元素
      numbers.erase(it);
  2. 删除范围内的元素:

    • erase 数可以删除指定范围内的元素。你需要提供一个表示范围的起始迭代器和结束迭代器。
      1
      2
      3
      4
      5
      std::list<int> myList = {1, 2, 3, 4, 5};
      auto start = myList.begin();
      auto end = myList.begin();
      std::advance(end, 3); //迭代器向前移动3个位置——注意这里std::list是双向链表
      myList.erase(start, end);
  3. 删除满足条件的元素:

    • 你可以使用 std::remove_if 算法与 erase 函数一起删除满足某个条件的元素。通常,这在容器中删除特定值或满足某个条件的元素时非常有用。(关于 remove_if 更多细节,见下文)
      1
      2
      std::vector<int> numbers = {1, 2, 3, 4, 5};
      numbers.erase(std::remove_if(numbers.begin(), numbers.end(), [](int x) { return x % 2 == 0; }), numbers.end());
  4. 删除所有元素:

    • 使用 clear 函数可以快速清空容器中的所有元素。
      1
      2
      std::vector<int> numbers = {1, 2, 3, 4, 5};
      numbers.clear(); // 删除所有元素,numbers变为空

需要注意的是, erase 函数可能会导致迭代器失效,因此在使用 erase 后,应谨慎操作迭代器。确保在删除元素后不再使用已失效的迭代器。此外,不同类型的容器可能有不同的参数和用法,因此使用前请查阅相关文档。工程实践中的具体例子可参考:[[SDK 相机断开重连偶现 crash]]

# i、remove_if

和上面的 remove 基本一致,只不过第三个参数由指定某个元素变成了指定某个条件。

std::remove_if 是 C++ 标准库中的一个算法,用于从容器中删除满足特定条件的元素,但它并不会真正地删除元素,而是将这些元素移到容器的末尾,并返回一个指向新的 “逻辑末尾” 位置的迭代器。

函数签名:

1
2
template <class ForwardIt, class UnaryPredicate>
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p);

参数:
firstlast 是表示范围的迭代器,它们定义了要进行操作的元素的范围。
p 是一个一元谓词(UnaryPredicate),它定义了要删除的条件。

操作:
std::remove_if 会遍历范围 [first, last) 中的元素,对于满足谓词 p 的元素,它会将它们移到范围的末尾,不满足条件的元素保持原位置。这就是为什么它不会真正删除元素,而只是重新排列它们。

返回值:
std::remove_if 返回一个指向新的 “逻辑末尾” 的迭代器,该迭代器之前的元素仍然保持不变,而迭代器之后的元素是被移动的元素,但它们的值是未定义的。这里的逻辑和 remove 函数一样。

由于 remove 之后返回的是新的逻辑尾迭代器,因此可以结合 erase 来使用,形式为 erase(remove_if(...), end)
示例:

1
2
3
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
numbers.erase(std::remove_if(numbers.begin(), numbers.end(), [](int x) { return x % 2 == 0; }), numbers.end());
// 在上述示例中,删除了所有偶数,numbers 现在包含 {1, 3, 5}。

# i、for_each

C++ 中的 for_each 函数是标准库 <algorithm> 头文件中的一个算法,用于对容器中的元素执行某个操作。它通常与函数对象(也称为谓词)一起使用,允许你在容器的每个元素上执行相同的操作。 for_each 函数的一般形式如下:

1
2
template<class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);

  • InputIterator 是一个迭代器,指定了要遍历的容器范围的起始和结束位置。
  • Function 是一个函数对象或谓词,用于定义要在容器的每个元素上执行的操作。
  • firstlast 参数指定了要遍历的范围,通常是容器的起始和结束迭代器。

for_each 函数的主要作用是对容器中的每个元素执行函数对象 f ,即对范围 [first, last) 中的每个元素调用函数对象 f 。这可以用于遍历容器并执行特定的操作,如打印元素、修改元素等。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <vector>
#include <algorithm>

void print_element(int element) {
std::cout << element << " ";
}

int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};

// 使用for_each函数调用print_element函数打印向量中的元素
std::for_each(numbers.begin(), numbers.end(), print_element);

std::cout << std::endl;

return 0;
}

# i、copy

std::copy 是 C++ 标准库中的一个算法函数,它用于将一个范围内的元素复制到另一个范围中。其基本用法如下:

1
2
3
#include <algorithm>
// ...
std::copy(first1, last1, first2);

  • first1last1 定义了源范围,表示要复制的元素范围是 [first1, last1) (左闭右开区间)。
  • first2 定义了目标范围,表示要将元素复制到的范围的起始位置。

在使用这个函数时,你需要确保源范围 [first1, last1) 和目标范围 [first2, ...) 之间有足够的空间来容纳要复制的元素。

这里假设 ii_iq 的大小足够大,以至于能够容纳 iiDataiqData 中的所有元素。如果 ii_iq 的大小不够,将导致未定义行为,可能引发程序崩溃或产生其他不确定的结果。

# i、replace

std::replace 是 C++ 标准库中的算法之一,用于在指定范围内替换元素的值。它的声明如下:

1
2
template< class ForwardIt, class T >
void replace( ForwardIt first, ForwardIt last, const T& old_value, const T& new_value );

这个算法在指定范围 [first, last) 内搜索所有等于 old_value 的元素,并将它们替换为 new_value

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
std::vector<int> numbers = {1, 2, 3, 2, 4, 2, 5};
// 使用 std::replace 将所有值为 2 的元素替换为 0
std::replace(numbers.begin(), numbers.end(), 2, 0);
// 打印替换后的向量
for (int num : numbers) {
std::cout << num << " ";
} return 0;
}

由于是模板函数,所以自然也可以在字符串中使用:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <string>
#include <algorithm>

int main() {
std::string fileName = "example.file.txt";

// 将所有的 '.' 替换为 '_'
std::replace(fileName.begin(), fileName.end(), '.', '_');

return 0;
}

另外,string 也内置了 replace 函数:(我们的原则是,类内有定义,优先使用类内函数)

1
2
//将字符串 myStr 的末尾 `.bin` 更换成 `.txt`
myStr.replace(myStr.end() - 4, myStr.end(), ".txt");

# i、rfind

在 C++ 中, rfind 是字符串类(如 std::string )的成员函数之一,用于在字符串中从后往前查找指定子字符串(或字符)的位置。它的基本用法如下:

1
size_t rfind(const std::string& str, size_t pos = npos) const;

  • str :要查找的子字符串。
  • pos :可选参数,指定开始查找的位置,默认值是 std::string::npos ,表示从字符串的末尾开始向前查找。

rfind 函数返回指定子字符串在原字符串中最后出现的位置。如果未找到子字符串,它将返回 std::string::npos ,通常表示未找到。

以下是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>

int main() {
std::string str = "Hello, world! Hello, C++!";
size_t found = str.rfind("Hello");

if (found != std::string::npos) {
std::cout << "Last occurrence of 'Hello' found at position " << found << std::endl;
} else {
std::cout << "'Hello' not found in the string." << std::endl;
}

return 0;
}

在这个示例中, rfind 函数查找字符串中最后出现的 "Hello",并返回其位置。如果找到,它会输出该位置,否则会提示未找到。

注意,这是从字符串开始的偏移量,而不是结束的偏移量。

参考链接:https://en.cppreference.com/w/cpp/string/basic_string/rfind

# i、hardware_concurrency

hardware_concurrency 是 C++ 标准库中的一个函数,属于 <thread> 头文件的一部分。它的主要作用是返回能够并发执行的(物理)线程数量。这通常反映了系统上的处理器核心数量。

下面是一个简单的使用例子:

1
2
3
4
5
6
7
8
#include <iostream>
#include <thread>

int main() {
unsigned int n = std::thread::hardware_concurrency();
std::cout << "This system can run " << n << " concurrent threads." << std::endl;
return 0;
}

当调用 std::thread::hardware_concurrency() 时,它会返回一个 unsigned int 值,该值表示系统上可以并发运行的最大线程数量。如果无法确定这个数量,它可能返回 0。

需要注意的是,返回的值只是一个建议值,不是硬性规定,具体的并发能力还受到操作系统的线程调度和其他资源限制的影响。

# i、sscanf

1
int sscanf(const char *str, const char *format, ...);

sscanf 用于从字符串中以指定格式读取信息,可视作 scanf 的重定向版本,源头由标准输入转变为字符串。

sscanf() reads its input from the character string pointed to  by str.

例如:

1
2
uint64_t utc_ts, utc_tns, gtc_ts, gtc_tns, data_size, offset;
sscanf(line.c_str(), "%lu %lu %s %lu %lu %lu %lu", &utc_ts, &utc_tns, topic, &data_size, &offset, &gtc_ts, &gtc_tns);

# i、emplace

模板函数,作用是在容器中的 position 位置用 args 这一包数据(可变参数)来原地构造一个元素。

1
template <class... Args>iterator emplace(const_iterator position, Args&&... args);

可以将其视作更优雅的 push_back、insert 方法,举例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class People {
public:
People()=default;

People(int n, double j) : m_a(n), m_b(j) {
cout << "def constructor" << endl;
}

People(const People &ppl) : m_a(ppl.m_a), m_b(ppl.m_b) {
cout << "cp constructor" << endl;
}

People(const People && ppl) : m_a(ppl.m_a), m_b(ppl.m_b) {
cout << "mv constructor" << endl;
}

People& operator=(const People& ppl) {
cout << "cp assign" << endl;
m_a = ppl.m_a;
m_b = ppl.m_b;
return *this;
}

private:
int m_a;
double m_b;
};

cout << "----------insert func----------" << endl;
vector<People> pplVec1;
pplVec1.insert(pplVec1.end(), People(1, 17.1)); //构造一个临时变量

cout << "----------insert func----------" << endl;
vector<People> pplVec2;
pplVec2.insert(pplVec2.end(), {2, 17.2}); //构造一个临时变量

cout << "----------emplace func----------" << endl;
vector<People> pplVec3;
pplVec3.emplace(pplVec3.end(), 3, 8.2); //无需构造临时变量

输出:
----------insert func----------
def constructor
mv constructor
----------insert func----------
def constructor
mv constructor
----------emplace func----------
def constructor

通过输出信息可知,emplace 少调用了一次拷贝构造。(有移动构造时会用移动构造代替拷贝构造)

注意,当拷贝构造函数和移动构造函数同时存在时,会优先调用移动构造函数。即,emplace 就地构造,省去一次临时变量的创建。其底层实现原理是 可变参数模板 (即...args)和 完美转发 ,这里不展开。 std::vector::emplace_back()  则是另一个显式的函数,不过是在尾部插入。

工作中实际应用场景:

1
2
3
4
5
6
7
8
9
10
template <class FrameVectorType>
typename FrameVectorType::iterator EmplaceMea(FrameVectorType &frame_vector)
{
//FRAMEWORK::NW("DO NOT forget to lock using board mutex");
BeginLock();
typename FrameVectorType::iterator it = frame_vector.emplace_back_itor_noblock();
EndLock();
it->clear();
return it;
}

更新于

请我喝杯咖啡吧~

Rick 微信支付

微信支付

Rick 支付宝

支付宝