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

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 iterator
    return ++_First;
}

// FUNCTION TEMPLATE next
template <class _InIt>
_NODISCARD _CONSTEXPR17 _InIt next(_InIt _First, _Iter_diff_t<_InIt> _Off = 1) { // increment iterator
    static_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 iterator
    return --_First;
}

// FUNCTION TEMPLATE prev
template <class _BidIt>
_NODISCARD _CONSTEXPR17 _BidIt prev(_BidIt _First, _Iter_diff_t<_BidIt> _Off = 1) { // decrement iterator
    static_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.kler.cn/news/367501.html

相关文章:

  • 我准备写一份Stable Diffusion入门指南-part1
  • 深入理解 Java 接口
  • 深入理解Python异常处理机制
  • 让你的 IDEA 使用更流畅 | IDEA内存修改
  • TensorFlow面试整理-模型部署与优化
  • 【HTML】之form表单元素详解
  • #深度学习:从基础到实践
  • 华为配置 之 划分VLAN
  • 蓝桥杯第二十场小白入门赛
  • 【作业6】基于CNN的XO识别
  • 为什么会有树这样的数据结构,使用树有什么好处 和其他数据结构对比
  • Qt:QtCreator使用
  • 可以拖动屏幕的简单页面播放示例
  • 深入探讨TCP/IP协议基础
  • 【C++】—— 模板进阶
  • 数字加% 循环后两个都变了只能深拷贝
  • 《计算机原理与系统结构》学习系列——处理器(中)
  • Linux:socket实现两个进程之间的通信
  • #单体到微服务架构服务演化过程
  • Mermaid流程图完全指南
  • 2024年10月25日练习(双指针算法)
  • Redis 主从同步 问题
  • python一键运行所有bat脚本
  • 机器学习(10.14-10.20)(Pytorch GRU的原理及其手写复现)
  • P1588 [USACO07OPEN] Catch That Cow S
  • Unity C#脚本的热更新