当前位置: 首页 > news >正文

C++标准库之std::begin、std::end、std::pre和std::next

目录

1.std::begin和std::end

1.1.begin()和end()参数为容器

1.2.begin()和end()参数为数组

1.3.用户自定义重载

2.std::pre和std::next

3.总结


1.std::begin和std::end

      std::begin()和std::end()是C++ STL中的函数模板,用于获取容器(数组、std::initializer_list、STL标准容器、std::string_view、std::array等)的起始和结束迭代器。它们提供了一种通用的方式来访问这些序列的边界,而不依赖于具体的容器类型。一般结合STL的算法一起使用,如:

int a[] = { 1, 3, 5, 2, 9, 6, 8 };
std::sort(std::begin(a), std::end(a));

  它们的定义如下:

// FUNCTION TEMPLATES begin AND end
template <class _Container>
_NODISCARD _CONSTEXPR17 auto begin(_Container& _Cont) -> decltype(_Cont.begin()) {return _Cont.begin();
}template <class _Container>
_NODISCARD _CONSTEXPR17 auto begin(const _Container& _Cont) -> decltype(_Cont.begin()) {return _Cont.begin();
}template <class _Container>
_NODISCARD _CONSTEXPR17 auto end(_Container& _Cont) -> decltype(_Cont.end()) {return _Cont.end();
}template <class _Container>
_NODISCARD _CONSTEXPR17 auto end(const _Container& _Cont) -> decltype(_Cont.end()) {return _Cont.end();
}template <class _Ty, size_t _Size>
_NODISCARD constexpr _Ty* begin(_Ty (&_Array)[_Size]) noexcept {return _Array;
}template <class _Ty, size_t _Size>
_NODISCARD constexpr _Ty* end(_Ty (&_Array)[_Size]) noexcept {return _Array + _Size;
}

从上面的定义来看:

1.1.begin()和end()参数为容器

        当将某个具体容器(比如 cont)作为参数分别传给 begin() 和 end() 函数时,其中 begin() 底层会执行 cont.begin() 语句,而 end() 底层会执行 cont.end() 语句,它们最终会将得到的迭代器作为函数的返回值反馈回来。

        当作用对象为容器时,end() 和 begin() 函数的语法格式是完全一样的,这里以 begin() 函数为例,有以下 2 种格式:
//① 非 const 修改的容器作为参数,begin() 函数返回的为非 const 类型的迭代器
template
auto begin (Container& cont)
//② 传入 const 修饰的容器,begin() 函数返回的为 const 类型的迭代器
template
auto begin (const Container& cont)

其中,cont 表示指定的容器;同时,函数会返回一个有特定指向的迭代器,且此迭代器的类型也取决于 cont 容器。
以上 2 种格式的区别仅在与传入的容器是否有 const 修饰,即如果有,则通过该函数获得的迭代器也有 const 修饰(不能用于修改容器中存储的数据);反之就没有。

#include <iostream>     // std::cout
#include <vector>       // std::vector, std::begin, std::end
using namespace std;
int main() {//创建并初始化 vector 容器std::vector<int> myvector{ 1,2,3,4,5 };//调用 begin() 和 end() 函数遍历 myvector 容器for (auto it = begin(myvector); it != end(myvector); ++it)cout << *it << ' ';return 0;
}//程序执行结果为:
//1 2 3 4 5//程序第 8 行中,begin(myvector) 等同于执行 myvector.begin(),
//而 end(myvector) 也等同于执行 myvector.end()。

1.2.begin()和end()参数为数组

        除了可以将指定容器作为参数传给 begin() 和 end() 之外,还可以指定数组作为参数传给它们。

        将指定数组传给 begin() 函数,其会返回一个指向该数组首个元素的指针;将指定数组传给 end() 函数,其会返回一个指向数组中最后一个元素之后位置的指针。

        同样,数组作为参数时,end() 函数的语法格式和 begin() 函数也完全一样,这里仅给出了 begin() 函数的语法格式:

template <class T, size_t N>
T* begin (T(&arr)[N]);

其中 T 为数组中存储元素的类型,N 为数组的长度;(&arr)[N] 表示以引用的方式传递数组作为参数。

#include <iostream>     // std::cout
#include <vector>       // std::vector, std::begin, std::end
using namespace std;
int main() {//定义一个普通数组int arr[] = { 1,2,3,4,5 };//创建一个空 vector 容器vector<int> myvector;//将数组中的元素添加到 myvector 容器中存储for (int *it = begin(arr); it != end(arr); ++it)myvector.push_back(*it);//输出 myvector 容器中存储的元素for (auto it = myvector.begin(); it != myvector.end(); ++it)cout << *it << ' ';return 0;
}
//程序执行结果为:
//1 2 3 4 5

举个例子:
注意程序中第 10 行,这里用整数指针 it 接收 begin(arr) 的返回值,同时该循环会一直循环到 it 指向 arr 数组中最后一个元素之后的位置。

1.3.用户自定义重载

可以为不暴露适合的 begin()和end()成员函数的类提供 begin 和end的自定义重载,从而能迭代它。标准库已提供下列重载:

std::begin(std::initializer_list)

(C++11)

std::end(std::initializer_list)

(C++11)

特化 std::begin和std::end
(函数模板)

std::begin(std::valarray)

(C++11)

std::end(std::valarray)

(C++11)

特化的 std::begin和std::end
(函数模板)

begin(std::filesystem::directory_iterator)end(std::filesystem::directory_iterator)

基于范围的 for 循环支持
(函数)

begin(std::filesystem::recursive_directory_iterator)end(std::filesystem::recursive_directory_iterator)

基于范围的 for 循环支持
(函数)

同 swap 的使用(描述于可交换 (Swappable) ), begin 函数在泛型语境中的使用等价于 using std::begin; begin(arg);,end 函数在泛型语境中的典型使用等价于 using std::end; end(arg); 这允许 ADL 为用户定义类型所选的重载和出现于同一重载集的标准库函数模板。

template<typename Container, typename Function>
void for_each(Container&& cont, Function f) {using std::begin;auto it = begin(cont);using std::end;auto end_it = end(cont);while (it != end_it) {f(*it);++it;}
}

2.std::pre和std::next

        在C++标准库中,std::prev 和 std::next 是两个常用的函数模板,用于在迭代器上进行向前或向后的操作。它们分别用于获取给定迭代器的前一个或后一个迭代器。这些函数模板定义在头文件 <iterator> 中。

         它们的定义如下:

// FUNCTION TEMPLATE _Next_iter
template <class _InIt>
constexpr _InIt _Next_iter(_InIt _First) { // increment iteratorreturn ++_First;
}// FUNCTION TEMPLATE next
template <class _InIt>
_NODISCARD _CONSTEXPR17 _InIt next(_InIt _First, _Iter_diff_t<_InIt> _Off = 1) { // increment iteratorstatic_assert(_Is_input_iter_v<_InIt>, "next requires input iterator");_STD advance(_First, _Off);return _First;
}// FUNCTION TEMPLATE _Prev_iter
template <class _BidIt>
constexpr _BidIt _Prev_iter(_BidIt _First) { // decrement iteratorreturn --_First;
}// FUNCTION TEMPLATE prev
template <class _BidIt>
_NODISCARD _CONSTEXPR17 _BidIt prev(_BidIt _First, _Iter_diff_t<_BidIt> _Off = 1) { // decrement iteratorstatic_assert(_Is_bidi_iter_v<_BidIt>, "prev requires bidirectional iterator");_STD advance(_First, -_Off);return _First;
}

注意:std::prev 和 std::next 要求迭代器至少是双向迭代器(BidirectionalIterator),这意味着它必须支持向前和向后移动。

以下是一个简单的示例,演示如何使用 std::prev 和 std::next

#include <iostream>  
#include <vector>  
#include <iterator>  int main() {  std::vector<int> vec = {1, 2, 3, 4, 5};  auto it = vec.begin();  // 使用 std::next  auto next_it = std::next(it, 2); // 指向 vec[2] 即 3  std::cout << "Element pointed by next_it: " << *next_it << std::endl;  // 使用 std::prev  auto prev_it = std::prev(next_it, 1); // 指向 vec[1] 即 2  std::cout << "Element pointed by prev_it: " << *prev_it << std::endl;  return 0;  
}

3.总结

  • std::begin 用于获取给定容器的起始迭代器。
  • std::end 用于获取给定容器的结束迭代器。
  • std::prev 用于获取给定迭代器的前一个迭代器。
  • std::next 用于获取给定迭代器的后一个迭代器。
  • 它们分别要求迭代器至少是双向迭代器(对于 std::prev)和前向迭代器(对于 std::next)。

这四个函数模板在遍历容器或处理迭代器时非常有用,可以简化代码并增加可读性。


http://www.mrgr.cn/news/59747.html

相关文章:

  • Java - 人工智能;SpringAI
  • Golang文件操作:读取与写入全攻略
  • vue3父组件控制子组件表单验证及获取子组件数值方法
  • 【MySQL】视图
  • # 渗透测试#安全见闻8 量子物理面临的安全挑战
  • day02|计算机网络重难点之HTTP请求报文和响应报文、HTTP的请求方式(方法字段)、GET请求和POST请求的区别
  • Maven 项目构建打包,如何引入本地 Jar 包?
  • 【Rust练习】18.特征 Trait
  • 人工智能与深度学习入门
  • 【K8S系列】Kubernetes 中 Service IP 地址和端口不匹配问题及解决方案【已解决】
  • Maven:详解 clean 和 install 命令的使用
  • promise+async/await+任务队列
  • 同步电机不同电流参考方向下的功率计算
  • Python毕业设计选题:基于Python的个性化旅游路线推荐系统-flask+vue
  • 位运算技巧
  • 玩转springboot之springboot属性绑定原理
  • 【C++奇遇记】C++中的基础知识(缺省参数,函数重载,引用)
  • 二进制搭建 Kubernetes v1.20
  • Kubernetes实战——DevOps集成SpringBoot项目
  • 深入了解嵌入式硬件设计
  • SSM-Springboot笔记(2)- SpringBoot常用开发技能
  • 自拍照片P西装领带的正装,用手机就可以搞定的方法
  • 二分查找法
  • linux-i2c驱动-ap3216c
  • 电机学习-SVPWM合成原理
  • InnoDB 存储引擎<二>页结构和行结构