c/c++开发,无可避免的模板编程实践(篇九)-c++11的新顺序容器

news2024/9/24 7:22:06

一、std::array数组容器

        1.1 数组的适配器-std::array

        std::array 是封装固定大小数组的容器,是c++11标准库新引入的顺序容器,定义于头文件 <array>。

template <class T,std::size_t N > struct array; 

        此容器是一个聚合类型,其语义等同于保有一个 C 风格数组 T[N] 的结构体。不同于 C 风格数组,它不会自动退化成 T* 。它在定义时,需要显式指定模板参数类型和非模板参数数值,它能作为聚合类型聚合初始化,只要有至多 N 个能转换成 T 的初始化器,可将 array 当做拥有 N 个同类型元素的元组:

 std::array<int, 3> a = {1,2,3}; 

        std::array 满足容器 (Container) 和可逆容器 (ReversibleContainer) 的要求,除了默认构造的 array 是非空的,以及进行交换的复杂度是线性,它满足连续容器 (ContiguousContainer) (C++17 起)的要求并部分满足序列容器 (SequenceContainer) 的要求。

         还记得在本课题的篇一的“3.4 非类型模板参数及缺省值”中讲述结构体模板内容:

#define RDC_SIZE 1024	//服务端从socket读取数据时的缓存大小
#define DATA_SIZE 512	//服务及客户端的数据传输(读取及写入)的缓存大小
 
template<int SIZE = DATA_SIZE>
struct TCP_Data
{
	TCP_Data() 
		: len(0)
	{
		memset(Buf,0,SIZE);
	};
    ...省略其他...
	unsigned char Buf[SIZE];
	int len;
};

        现在将他改造成一个类似std::array结构体,及数组类型为T,长度为SIZE:

#include <cstring>
#include <cassert>

template<typename T,int SIZE>
struct my_array
{
	my_array() 
	{
		memset(Buf,0,SIZE);
	};
    ~my_array()
	{
	};
    T& operator[]( const int pos )
    {
        assert(pos >= 0 && pos < SIZE);
        return Buf[pos];
    }
    T& at(const int pos)
    {
        assert(pos >= 0 && pos < SIZE);
        return Buf[pos];
    }
    //其他实现
	T Buf[SIZE];
};
 
void myarray_test(void)
{
    //调用
    my_array<int,3> a;          //int类型数据,长度大小3
    a[0] = 9;
    a.at(1) = 10;
    std::cout << "a.at(0)=" << a.at(0) << "\n";
    std::cout << "a[1]=" << a[1] << "\n";
};

        这就是std::array容器内部的数据组织结构,通过可以说它就是一个数组适配器,在一个数组存储结构基础上,提供了元素访问、迭代器、容量、操作符等功能函数以及构造、析构、赋值等类成员。当然,除了上述一下成员及成员函数以为,标准库还std::array容器类模板提供了一些辅助函数、比较函数等。

        1.2 认识std::array容器(c++11起)

        std::array容器本质上是一个结构体模板,它结合了 C 风格数组的性能、可访问性与容器的优点,比如可获取大小、支持赋值、随机访问迭代器等。其功能函数如下:

成员类型                     定义 
value_type                   T 
size_type                    std::size_t 
difference_type              std::ptrdiff_t 
reference                    value_type& 
const_reference              const value_type& 
pointer                      value_type* 
const_pointer                const value_type* 
iterator//->
/*
[1]指向 value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 及老式连续迭代器 (LegacyContiguousIterator)  
[2]指向 value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 及老式连续迭代器 (LegacyContiguousIterator) 且为字面类型 (LiteralType) (C++20 前) 
[3]指向 value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 、contiguous_iterator 及常量表达式迭代器 (ConstexprIterator) (C++20 起) 
*/
const_iterator//-> 
/*
[1]指向 const value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 及老式连续迭代器 (LegacyContiguousIterator)
[2]指向 const value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 及老式连续迭代器 (LegacyContiguousIterator) 且为字面类型 (LiteralType) (C++20 前) 
[3]指向 const value_type 的老式随机访问迭代器 (LegacyRandomAccessIterator) 、contiguous_iterator 及常量表达式迭代器 (ConstexprIterator) (C++20 起) 
*/
reverse_iterator               std::reverse_iterator<iterator> 
const_reverse_iterator         std::reverse_iterator<const_iterator> 

成员函数
(构造函数)    //(隐式声明)遵循聚合初始化的规则初始化array(注意默认初始化可以导致非类的T的不确定值)(公开成员函数) 
(析构函数)    //(隐式声明) 销毁 array 的每个元素(公开成员函数) 
operator=    //(隐式声明)以来自另一 array 的每个元素重写 array 的对应元素(公开成员函数) 

元素访问
at           //(C++11) 访问指定的元素,同时进行越界检查(公开成员函数) 
operator[]   //(C++11) 访问指定的元素(公开成员函数) 
front        //(C++11) 访问第一个元素(公开成员函数) 
back         //(C++11) 访问最后一个元素(公开成员函数) 
data         //(C++11) 直接访问底层数组(公开成员函数) 

迭代器
begin        //(C++11)返回指向起始的迭代器(公开成员函数) 
cbegin       //(C++11)返回指向起始的迭代器(公开成员函数) 
end          //(C++11)返回指向末尾的迭代器(公开成员函数) 
cend         //(C++11)返回指向末尾的迭代器(公开成员函数) 

rbegin       //(C++11)返回指向起始的逆向迭代器(公开成员函数) 
crbegin      //(C++11)返回指向起始的逆向迭代器(公开成员函数) 

rend         //(C++11)返回指向末尾的逆向迭代器(公开成员函数) 
crend        //(C++11)返回指向末尾的逆向迭代器(公开成员函数) 

容量
empty        //(C++11) 检查容器是否为空(公开成员函数) 
size         //(C++11) 返回容纳的元素数(公开成员函数) 
max_size     //(C++11) 返回可容纳的最大元素数(公开成员函数) 

操作
fill         //(C++11) 以指定值填充容器(公开成员函数) 
swap         //(C++11) 交换内容(公开成员函数) 

非成员函数
operator==    //按照字典顺序比较 array 中的值(函数模板)
operator!=    //(C++20 中移除)
operator<     //(C++20 中移除)
operator<=    //(C++20 中移除)
operator>     //(C++20 中移除)
operator>=    //(C++20 中移除)       
operator<=>   //(C++20) 

std::get(std::array)  //访问 array 的一个元素(函数模板) 
std::swap(std::array) //(C++11)特化 std::swap 算法(函数模板) 
to_array              //(C++20)从内建数组创建 std::array 对象(函数模板) 

辅助类
std::tuple_size<std::array>    //(C++11)获得 array 的大小(类模板特化) 
std::tuple_element<std::array> //(C++11)获得 array 元素的类型(类模板特化)

        std::array容器定义在标准库头文件 <array>中,本质上就是一个包含模板参数T和非模板参数size_t的结构体,放置在std命名空间内:

namespace std {
  template<class T, size_t N>
  struct array {
    // 类型
    using value_type             = T;
    using pointer                = T*;
    using const_pointer          = const T*;
    using reference              = T&;
    using const_reference        = const T&;
    using size_type              = size_t;
    using difference_type        = ptrdiff_t;
    using iterator               = /* 由实现定义 */;
    using const_iterator         = /* 由实现定义 */;
    using reverse_iterator       = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
    // 聚合类型无显式的构造/复制/销毁
 
    constexpr void fill(const T& u);
    constexpr void swap(array&) noexcept(is_nothrow_swappable_v<T>);
 
    // 迭代器
    constexpr iterator               begin() noexcept;
    constexpr const_iterator         begin() const noexcept;
    constexpr iterator               end() noexcept;
    constexpr const_iterator         end() const noexcept;
 
    constexpr reverse_iterator       rbegin() noexcept;
    constexpr const_reverse_iterator rbegin() const noexcept;
    constexpr reverse_iterator       rend() noexcept;
    constexpr const_reverse_iterator rend() const noexcept;
 
    constexpr const_iterator         cbegin() const noexcept;
    constexpr const_iterator         cend() const noexcept;
    constexpr const_reverse_iterator crbegin() const noexcept;
    constexpr const_reverse_iterator crend() const noexcept;
 
    // 容量
    [[nodiscard]] constexpr bool empty() const noexcept;
    constexpr size_type size() const noexcept;
    constexpr size_type max_size() const noexcept;
 
    // 元素访问
    constexpr reference       operator[](size_type n);
    constexpr const_reference operator[](size_type n) const;
    constexpr reference       at(size_type n);
    constexpr const_reference at(size_type n) const;
    constexpr reference       front();
    constexpr const_reference front() const;
    constexpr reference       back();
    constexpr const_reference back() const;
 
    constexpr T *       data() noexcept;
    constexpr const T * data() const noexcept;
  };
 
  template<class T, class... U>
    array(T, U...) -> array<T, 1 + sizeof...(U)>;
}

        前面也讲述到std::array类模板能作为聚合类型聚合初始化:

void construct_test(void)
{
    // 用聚合初始化构造
    std::array<int, 3> a1{ {1, 2, 3} }; // CWG 1270 前的 C++11 中要求双花括号
                                        // ( C++11 之后的版本和 C++14 起不要求)
    std::array<int, 3> a2 = {1, 2, 3};  // = 后决不要求双花括号
    std::array<std::string, 2> a3 = { std::string("a"), "b" };
 
    // 支持容器操作
    std::sort(a1.begin(), a1.end());
    std::reverse_copy(a2.begin(), a2.end(), 
                      std::ostream_iterator<int>(std::cout, " "));
 
    std::cout << '\n';
 
    // 支持带范围 for 循环
    for(const auto& s: a3)
        std::cout << s << ' ';
    std::cout << '\n';
};

        1.3 std::array容器使用

        该类模板也和其他顺序容器一样,提供了下标访问、at访问、front访问、back访问等元素访问方式,在零长 array 上调用 front() 或 back() 是未定义的:

void getval_test(void)
{
    std::array<int,6> data = { 1, 2, 4, 5, 5, 6 };
    // Set element 1
    data.at(1) = 88;
    // Read element 2
    std::cout << "Element at index 2 has value " << data.at(2) << '\n';
    std::cout << "data size = " << data.size() << '\n';
 
    try {
        // Set element 6
        data.at(6) = 666;
    } catch (std::out_of_range const& exc) {
        std::cout << exc.what() << '\n';
    }
 
    // Print final values
    std::cout << "data:";
    for (int elem : data)
        std::cout << " " << elem;
    std::cout << '\n';
    //
    std::array<char, 6> letters {'o', 'm', 'g', 'w', 't', 'f'};
    if (!letters.empty()) {
        std::cout << "The last character is: " << letters.back() << '\n';
    } 
};

        同时,与数组结构最大的区别就是提供了正向和逆向的迭代器支持,当其长度为零时 array ( N == 0 )有特殊情况。此时, array.begin() == array.end() ,并拥有某个唯一值:

void iterator_test_array(void)
{
    std::cout << std::boolalpha;
 
    std::array<int, 0> empty;
    std::cout << "1) "
              << (empty.begin() == empty.end()) << ' '     // true
              << (empty.cbegin() == empty.cend()) << '\n'; // true
    // *(empty.begin()) = 42; // => 运行时的未定义行为
 
 
    std::array<int, 4> numbers{5, 2, 3, 4};
    std::cout << "2) "
              << (numbers.begin() == numbers.end()) << ' '    // false
              << (numbers.cbegin() == numbers.cend()) << '\n' // false
              << "3) "
              << *(numbers.begin()) << ' '    // 5
              << *(numbers.cbegin()) << '\n'; // 5
 
    *numbers.begin() = 1;
    std::cout << "4) " << *(numbers.begin()) << '\n'; // 1
    // *(numbers.cbegin()) = 42; // 编译时错误:
                                 // 只读变量不可赋值
 
    // 打印所有元素
    std::cout << "5) ";
    std::for_each(numbers.cbegin(), numbers.cend(), [](int x) {
       std::cout << x << ' ';
    });
    std::cout << '\n';
    #if(__cplusplus>=201703L)
    //c++17
    constexpr std::array<char,3> arrs{'A', 'B', 'C'};
    static_assert(arrs.begin() != arrs.end());   // OK
    static_assert(arrs.cbegin() != arrs.cend()); // OK
    static_assert(*arrs.begin() == 'A');              // OK
    static_assert(*arrs.cbegin() == 'A');             // OK
    // *arrs.begin() = 'Z'; // 编译时错误:只读变量不可赋值
    #endif
};

        下来我们验证一下std::array容器与同等大小的数组的效率问题,数组的功能有限,我们只通过对元素赋值操作演示:

#include <chrono>
#include <random>
#include <iterator>
#include <iostream>
#include <array>

void speed_test(void)
{
    const unsigned long sizel = 100000;//不能设值太大,数组分配会有问题
    int vec_test[sizel] = {0};
    std::array<int, sizel> arr_test{0};
    auto start = std::chrono::system_clock::now();
    for(int row=0; row<100; row++){
        for (size_t i = 0; i < sizel; i++)
        {
            vec_test[i] =(rand()/sizel);
        }
    }
    auto end = std::chrono::system_clock::now();
    std::chrono::duration<double,std::milli> diff = end-start;
    std::cout << "vec_test diff.count() = " <<  diff.count() << "ms\n";
    start = std::chrono::system_clock::now();
    for(int row=0; row<100; row++)
    {
        for (size_t i = 0; i < sizel; i++)
        {
            arr_test[i] =(rand()/sizel);
        }
    }
    end = std::chrono::system_clock::now();
    diff = end-start;
    std::cout << "arr_test diff.count() = " <<  diff.count() << "ms\n";
    start = std::chrono::system_clock::now();
    std::array<int, sizel>::iterator iter = arr_test.begin();
    for(int row=0; row<100; row++)
    {
        for (iter = arr_test.begin(); iter!=arr_test.end(); iter++)
        {
            *iter = (rand()/sizel);
        }
    }
    end = std::chrono::system_clock::now();
    diff = end-start;
    std::cout << "arr_test iterator diff.count() = " <<  diff.count() << "ms\n";
};

        编译测试比较示例,程序输出如下,可以看到执行100*100000的元素随机赋值,std::array也能维持和纯数组在毫秒级内,缺带来正反迭代器、边界检查、容量检查等扩展:

二、std::forward_list-单链表

        2.1 认识std::forward_list容器

        std::forward_list容器是c++11标准新增的顺序容器,是支持从容器中的任何位置快速插入和移除元素的容器。与 std::list 相比,它实现为单链表,此容器在不需要双向迭代时提供更有效地利用空间的存储。该容器定义于头文件 <forward_list>中。

template< class T, class Allocator = std::allocator<T> >
class forward_list;                                                 //(C++11 起) 

namespace pmr { 
    template <class T> using forward_list = 
    std::forward_list<T, std::pmr::polymorphic_allocator<T> >       // (C++17 起) 
} 

        std::forward_list容器是一个类模板,T - 元素的类型,其需要支持到容器的实际操作,如比较、赋值等;模板参数 Allocator - 用于获取/释放内存及构造/析构内存中元素的分配器。类型必须满足分配器 (Allocator) 的要求。若 Allocator::value_type 与 T 不同则行为未定义 (C++20 前)程序非良构 (C++20 起)。std::forward_list 满足容器 (Container) (除了 operator== 的复杂度始终为线性和 size 函数)、知分配器容器 (AllocatorAwareContainer) 和序列容器 (SequenceContainer) 的要求。

        std::forward_list定义对象实例时,需要显式指明元素类型,支持从各种数据源构造新容器,可选地使用用户提供的分配器 alloc

/*默认构造函数。构造拥有默认构造的分配器的空容器*/
forward_list();                                     //空表 
/*构造拥有给定分配器 alloc 的空容器,
*alloc-用于此容器所有内存分配的分配器 */
explicit forward_list( const Allocator& alloc );    //指定分配器  
/*构造拥有 count 个有值 value 的元素的容器。
*count - 容器的大小 ,value - 以之初始化容器元素的值 */
forward_list( size_type count, 
              const T& value,
              const Allocator& alloc = Allocator()); //(C++11 起)
/*构造拥有个 count 默认插入的 T 实例的容器。不进行复制*/
explicit forward_list(size_type count);              //(C++11 起)(C++14 前) 
explicit forward_list(size_type count, const Allocator& alloc = Allocator());//(C++14 起) 
/*构造拥有范围 [first, last) 内容的容器
*first, last - 复制元素的来源范围 */
template< class InputIt > 
forward_list( InputIt first, InputIt last,
              const Allocator& alloc = Allocator() );              //(C++11 起) 
/*复制构造函数。构造拥有 other 内容的容器
*other - 用作初始化容器元素来源的另一容器 */
forward_list( const forward_list& other );                         //(C++11 起) 
/*构造拥有 other 内容的容器,以 alloc 为分配器*/
forward_list( const forward_list& other, const Allocator& alloc ); //(C++11 起) 
/* 移动构造函数。用移动语义构造拥有 other 内容的容器。分配器通过属于 other 的分配器移动构造获得*/
forward_list( forward_list&& other );                              //(C++11 起) 
/*有分配器扩展的移动构造函数。以 alloc 为新容器的分配器,从 other 移动内容;若 alloc != other.get_allocator() ,则它导致逐元素移动*/
forward_list( forward_list&& other, const Allocator& alloc );      //(C++11 起)
/*构造拥有 initializer_list init 内容的容器
*init - 用作初始化元素来源的 initializer_list */
forward_list( std::initializer_list<T> init,
               const Allocator& alloc = Allocator() );             //(C++11 起) 

        std::forward_list提供了多种构造方式定义对象实例,满足不同业务场景需要:

#include <iostream>
#include <string>
#include <forward_list>

template<typename T>
std::ostream& operator<<(std::ostream& s, const std::forward_list<T>& v)
{
    s.put('[');
    char comma[3] = {'\0', ' ', '\0'};
    for (const auto& e : v) {
        s << comma << e;
        comma[0] = ',';
    }
    return s << ']';
};

void construction_test(void)
{
    // C++11 初始化器列表语法:
    std::forward_list<std::string> words1 {"the", "frogurt", "is", "also", "cursed"};
    std::cout << "words1: " << words1 << '\n';
 
    // words2 == words1
    std::forward_list<std::string> words2(words1.begin(), words1.end());
    std::cout << "words2: " << words2 << '\n';
 
    // words3 == words1
    std::forward_list<std::string> words3(words1);
    std::cout << "words3: " << words3 << '\n';
 
    // words4 为 {"Mo", "Mo", "Mo", "Mo", "Mo"}
    std::forward_list<std::string> words4(5, "Mo");
    std::cout << "words4: " << words4 << '\n';    
};

        可以通过=操作符赋值给容器,也可以assign将值赋给容器

void assign_test(void)
{
    std::forward_list<int> l = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    for (int c : l) {
        std::cout << c << ' ';
    }
    std::forward_list<char> characters;
    characters.assign(5, 'a');
    for (char c : characters) {
        std::cout << c << ' ';
    } 
    characters.assign({'\n', 'C', '+', '+', '1', '1', '\n'});
    for (char c : characters) {
        std::cout << c;
    }
};

        2.2 std::forward_list容器使用

        std::forward_list提供了元素访问、迭代器、修改器、操作符等成员变量,也提供比较、删除、交换元素的辅助函数模板,其大体上和双链表std::list差不多:

成员类型                     定义 
value_type                   T 
allocator_type               Allocator 
size_type                    无符号整数类型(通常是 std::size_t ) 
difference_type              有符号整数类型(通常是 std::ptrdiff_t ) 
reference                    value_type& 
const_reference              const value_type& 
pointer                      std::allocator_traits<Allocator>::pointer 
const_pointer                std::allocator_traits<Allocator>::const_pointer 
iterator                     指向 value_type 的常老式向前迭代器 (LegacyForwardIterator) 
const_iterator               指向 const value_type 的老式向前迭代器 (LegacyForwardIterator) 

成员函数
(构造函数)                (C++11)  构造 forward_list(公开成员函数) 
(析构函数)                (C++11)  析构 forward_list(公开成员函数) 
operator=                (C++11)  赋值给容器(公开成员函数) 
assign                   (C++11)  将值赋给容器(公开成员函数) 
get_allocator            (C++11)返回相关的分配器 (公开成员函数) 

元素访问
front (C++11)  访问第一个元素(公开成员函数) 

迭代器
before_begin          (C++11)  返回指向第一个元素之前迭代器(公开成员函数) 
cbefore_begin

begin                 (C++11) 返回指向起始的迭代器(公开成员函数) 
cbegin

end                   (C++11) 返回指向末尾的迭代器(公开成员函数) 
cend

容量
empty                 (C++11) 检查容器是否为空(公开成员函数) 
max_size              (C++11) 返回可容纳的最大元素数(公开成员函数) 

修改器
clear                 (C++11) 清除内容(公开成员函数) 
insert_after          (C++11) 在某个元素后插入新元素(公开成员函数) 
emplace_after         (C++11) 在元素后原位构造元素(公开成员函数) 
erase_after           (C++11) 擦除元素后的元素(公开成员函数) 
push_front            (C++11) 插入元素到容器起始(公开成员函数) 
emplace_front         (C++11) 在容器头部原位构造元素(公开成员函数) 
pop_front             (C++11) 移除首元素(公开成员函数) 
resize                (C++11) 改变容器中可存储元素的个数(公开成员函数) 
swap                  (C++11) 交换内容(公开成员函数) 

操作
merge                 (C++11) 合并二个已排序列表(公开成员函数) 
splice_after          (C++11) 从另一 forward_list 移动元素(公开成员函数) 
removeremove_if       (C++11) 移除满足特定标准的元素(公开成员函数) 
reverse               (C++11) 将该链表的所有元素的顺序反转(公开成员函数) 
unique                (C++11) 删除连续的重复元素(公开成员函数) 
sort                  (C++11) 对元素进行排序(公开成员函数) 

非成员函数
operator==        按照字典顺序比较 forward_list 中的值(函数模板) 
operator!=        (C++20 中移除)
operator<         (C++20 中移除)
operator<=        (C++20 中移除)    
operator>         (C++20 中移除)
operator>=        (C++20 中移除)
operator<=>       (C++20)

std::swap(std::forward_list)    (C++11)  特化 std::swap 算法(函数模板) 
erase(std::forward_list)        (C++20) 擦除所有满足特定判别标准的元素(函数模板) 
erase_if(std::forward_list) 

         std::forward_list容器是不支持快速随机访问的单链表,定义在标准库头文件 <forward_list>中,本质上就是一个包含模板参数T和另一个模板参数std::allocator<T> 类模板,放置在std命名空间内:

namespace std {
  template<class T, class Allocator = allocator<T>>
  class forward_list {
  public:
    // 类型
    using value_type      = T;
    using allocator_type  = Allocator;
    using pointer         = typename allocator_traits<Allocator>::pointer;
    using const_pointer   = typename allocator_traits<Allocator>::const_pointer;
    using reference       = value_type&;
    using const_reference = const value_type&;
    using size_type       = /* 由实现定义 */;
    using difference_type = /* 由实现定义 */;
    using iterator        = /* 由实现定义 */;
    using const_iterator  = /* 由实现定义 */;
 
    // 构造/复制/销毁
    forward_list() : forward_list(Allocator()) { }
    explicit forward_list(const Allocator&);
    explicit forward_list(size_type n, const Allocator& = Allocator());
    forward_list(size_type n, const T& value, const Allocator& = Allocator());
    template<class InputIt>
      forward_list(InputIt first, InputIt last, const Allocator& = Allocator());
    forward_list(const forward_list& x);
    forward_list(forward_list&& x);
    forward_list(const forward_list& x, const Allocator&);
    forward_list(forward_list&& x, const Allocator&);
    forward_list(initializer_list<T>, const Allocator& = Allocator());
    ~forward_list();
    forward_list& operator=(const forward_list& x);
    forward_list& operator=(forward_list&& x)
      noexcept(allocator_traits<Allocator>::is_always_equal::value);
    forward_list& operator=(initializer_list<T>);
    template<class InputIt>
      void assign(InputIt first, InputIt last);
    void assign(size_type n, const T& t);
    void assign(initializer_list<T>);
    allocator_type get_allocator() const noexcept;
 
    // 迭代器
    iterator before_begin() noexcept;
    const_iterator before_begin() const noexcept;
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
 
    const_iterator cbegin() const noexcept;
    const_iterator cbefore_begin() const noexcept;
    const_iterator cend() const noexcept;
 
    // 容量
    [[nodiscard]] bool empty() const noexcept;
    size_type max_size() const noexcept;
 
    // 元素访问
    reference front();
    const_reference front() const;
 
    // 修改器
    template<class... Args> reference emplace_front(Args&&... args);
    void push_front(const T& x);
    void push_front(T&& x);
    void pop_front();
 
    template<class... Args>
      iterator emplace_after(const_iterator position, Args&&... args);
    iterator insert_after(const_iterator position, const T& x);
    iterator insert_after(const_iterator position, T&& x);
 
    iterator insert_after(const_iterator position, size_type n, const T& x);
    template<class InputIt>
      iterator insert_after(const_iterator position, InputIt first, InputIt last);
    iterator insert_after(const_iterator position, initializer_list<T> il);
 
    iterator erase_after(const_iterator position);
    iterator erase_after(const_iterator position, const_iterator last);
    void swap(forward_list&)
      noexcept(allocator_traits<Allocator>::is_always_equal::value);
 
    void resize(size_type sz);
    void resize(size_type sz, const value_type& c);
    void clear() noexcept;
 
    // forward_­list 操作
    void splice_after(const_iterator position, forward_list& x);
    void splice_after(const_iterator position, forward_list&& x);
    void splice_after(const_iterator position, forward_list& x, const_iterator i);
    void splice_after(const_iterator position, forward_list&& x, const_iterator i);
    void splice_after(const_iterator position, forward_list& x,
                      const_iterator first, const_iterator last);
    void splice_after(const_iterator position, forward_list&& x,
                      const_iterator first, const_iterator last);
 
    size_type remove(const T& value);
    template<class Predicate> size_type remove_if(Predicate pred);
 
    size_type unique();
    template<class BinaryPredicate> size_type unique(BinaryPredicate binary_pred);
 
    void merge(forward_list& x);
    void merge(forward_list&& x);
    template<class Compare> void merge(forward_list& x, Compare comp);
    template<class Compare> void merge(forward_list&& x, Compare comp);
 
    void sort();
    template<class Compare> void sort(Compare comp);
 
    void reverse() noexcept;
  };
 
  template<class InputIt, class Allocator = allocator</*iter-value-type*/<InputIt>>>
    forward_list(InputIt, InputIt, Allocator = Allocator())
      -> forward_list</*iter-value-type*/<InputIt>, Allocator>;
 
  // 交换
  template<class T, class Allocator>
    void swap(forward_list<T, Allocator>& x, forward_list<T, Allocator>& y)
      noexcept(noexcept(x.swap(y)));
}

        std::allocator 类模板定义于头文件 <memory>,是所有标准库容器所用的默认分配器 (Allocator) ,若不提供用户指定的分配器。默认分配器无状态,即任何给定的 allocator 实例可交换、比较相等,且能解分配同一 allocator 类型的任何其他实例所分配的内存。


template< class T > struct allocator;
template<> struct allocator<void>; //(C++17 中弃用)(C++20 中移除) 

        std::forward_list容器和其他容器一样支持iterator,通过迭代器可以实现容器遍历及元素访问:

void iterator_test(void)
{
    //
    std::forward_list<int> nums {1, 2, 4, 8, 16};
    auto print = [](const int& n) { std::cout << " " << n; };
    std::cout << "Before for_each:";
    std::for_each(nums.begin(), nums.end(), print);
    std::cout << '\n';
    //out:Before for_each: 1 2 4 8 16
    std::forward_list<std::string> fruits {"orange", "apple", "raspberry"};
    std::forward_list<char> empty;

    // 求和 forward_list nums 中的所有整数(若存在),仅打印结果。
    std::cout << "Sum of nums: " <<
            std::accumulate(nums.begin(), nums.end(), 0) << "\n";
    //out:Sum of nums: 31
    // 打印 forward_list fruits 中的首个 fruis ,不检查是否有一个。
    std::cout << "First fruit: " << *fruits.begin() << "\n";    //First fruit: orange
    if (empty.begin() == empty.end())
            std::cout << "forward_list 'empty' is indeed empty.\n";    //forward_list 'empty' is indeed empty.
};

        容器支持数据插入、删除操作,在链表内或跨数个链表添加、移除和移动元素,不会非法化当前指代链表中其他元素的迭代器。然而,在从链表移除元素(通过 erase_after )时,指代对应元素的迭代器或引用会被非法化。

void modify_test(void)
{
    std::forward_list<int> l = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    l.push_front(0);    //元素 value 到容器起始
    l.emplace_front(0); //插入新元素到容器起始。
    l.insert_after(l.begin(),10);         //在容器中的指定位置后插入元素。
    l.emplace_after(l.before_begin(),100);//在容器中的指定位置后插入新元素。
    for (int c : l) {
        std::cout << c << ' ';
    }
    std::cout << '\n';
    //out: 100 0 10 0 1 2 3 4 5 6 7 8 9
    //    l.erase( l.begin() ); // 错误:无要擦除的元素
 
    l.erase_after( l.before_begin() ); // 移除首元素
    l.pop_front();
    for( auto n : l ) {
        std::cout << n << " ";
    }
    std::cout << '\n';
    //out: 10 0 1 2 3 4 5 6 7 8 9
    auto fi= std::next( l.begin() );
    auto la= std::next( fi, 3 );
 
    l.erase_after( fi, la );
 
    for( auto n : l ) std::cout << n << " ";
    std::cout << '\n';  
    //out: 10 0 3 4 5 6 7 8 9
    //clear
    auto print = [](const int& n) { std::cout << " " << n; };
 
    std::cout << "Before clear:";
    std::for_each(l.begin(), l.end(), print);
    std::cout << '\n';
    //out: Before clear: 10 0 3 4 5 6 7 8 9
    std::cout << "Clear\n";
    l.clear();
    
    std::cout << "After clear:";
    std::for_each(l.begin(), l.end(), print);
    std::cout << '\n';
    //out: After clear:
};

        更多的成员还是用法就不一一展示了,大部分用法都可以参考std::list的用法,除了不支持逆序迭代外。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/400363.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatPDF解放双手帮你解读PDF文档

一、先介绍一下吧 chatPDF是一个解读pdf文档的AI模型&#xff0c;然后封装出来的工具。如论文、合同、文书、书籍等&#xff0c;只要是PDF都能搞定&#xff0c;可支持120页【2023.3.9】的文件。据说之前支持200页&#xff0c;反正在变 最新爆火的ChatPDF&#xff0c;短短5天就…

nginx 主动健康检查搭建详解(nginx_upstream_check_module)

版本信息 nginx: 1.21 1.下载nginx_upstream_check_module模块 nginx_upstream_check_module-master.zip wget https://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master 解压到 2. 安装nginx 略 3. 补丁安装 由于我这边安装nginx版本为nginx1.21…

你是使用什么工具调试 golang 程序的?

写过 C/C 的都是到&#xff0c;调试程序的时候通常使用 gdb 工具来进行调试&#xff0c;用起来可爽了&#xff0c;那么 gdb 是否也适合 golang 程序的调试的 我个人到是通常使用 dlv 来进行 golang 程序的调试&#xff0c;分享一波 dlv 是什么&#xff0c;全称 Delve Delve …

KiCad 编译

KiCad 编译 因为最新项目需要&#xff0c;所以看了一下KiCad的编译&#xff0c;这里介绍的是64位电脑的编译&#xff0c;32位小伙伴请绕道官网看教程呦。 您可以在KiCad内查看基本的编译教程。 我这里也是参考的官网编译教程进行的编译&#xff0c;接下来让我们一起看看吧。…

论文 | 期刊 | 专业名词解释

文章目录1. EI2. IEEE Xplore3. CN期刊3.2 CN期刊后面的数字代表什么3. SCI3.1 影响因子先立个帖子&#xff0c;后续用到的话随时更新1. EI 工程索引(EI)是由美国工程信息公司(Engineering information Inc.)编辑出版&#xff0c;历史上最悠久的一部大型综合性检索工具。 《工…

03 SWMM快速入门案例的设施参数设置与批量设置

文章目录1 雨量计1.1 雨量计基础设置1.2 雨量计数据来源2 汇水区2.1 参数讲解2.2 设置结果3 检查井3.1 参数讲解3.2 批量设置4 管道4.1 参数讲解4.2 设置结果5 出水口上一篇博客中我们已经完成了各类设施的绘制&#xff0c;本节对他们的参数进行设置1 雨量计 1.1 雨量计基础设…

第一章 C语言:数据存储

一、大小端存储大端存储&#xff1a;数据的低位字节存储在高地址小端存储&#xff1a;数据的低位字节存储在低地址不同编译器有不同的存储方式int a 10; char* p (char*)&a; printf("%x\n", *p); // a ---> 0000000a //0000 0000 0000 0000 0000 0…

教学场景应用视频试看预览功能

html5播放器视频预览功能效果 - 视频预览代码示例预播放一小段时间的视频内容&#xff0c;比如3分钟&#xff0c;然后引导用户付费观看或注册会员观看完整视频。原理&#xff1a;视频播放结束&#xff0c;执行s2j_onPlayOver()函数&#xff0c;显示提示信息或对话框&#xff0c…

Altium Designer(AD)软件使用记录03-AD软件中各层定义

Altium Designer(AD)软件使用记录03-AD软件中各层定义 重点&#xff1a; 1、常用的信号层&#xff1a;顶层&#xff0c;底层层&#xff0c;中间正片层&#xff0c;中间负片层 2、机械1层作为板框层&#xff0c;机械13层作为3D防止层&#xff0c;其他的机械层很少用 3、顶层阻焊…

AVL树详解+模拟实现

1&#xff1a;概念当数据有序&#xff0c;二叉搜索树将趋近于单叉树&#xff0c;查找元素相当于在顺序表中查找元素&#xff0c;效率低下&#xff0c;两位俄罗斯数学家G.M.Adelson-Velskii和E.M.Landis创建了AVL树。特性如下&#xff1a; 左右子树高度差的绝对值不超过1左右子树…

Django/Vue实现在线考试系统-06-开发环境搭建-Visual Studio Code安装

1.0 VS Code下载和安装 Visual Studio Code,简称 VS Code,是由微软公司开发的 IDE 工具。与微软其他 IDE(如 Visual Studio)不同的是,Visual Studio Code 是跨平台的,可以安装在 Windows、Linux 和 macOS平台上运行。不仅如此, Visual Studio Code 没有限定只能开发特定…

Revit中如何添加一个新的管道直径

有些时候项目当中会遇到一些管径比较小的管道&#xff0c;但是在直径中又没有适合的&#xff0c;怎么办?很简单&#xff0c;跟紧以下几个步理就可以了。 首先&#xff0c;我们拿一个管段为“铁&#xff0c;铸铁30”的为例子&#xff0c;如图1所示&#xff0c;系统中这管段是没…

1.数据结构的研究

数据结构很重要&#xff01; 数据结构很重要&#xff01;! 数据结构很重要&#xff01;! ! 思考 1.数据结构研究的内容有哪些&#xff1f;&#xff08;What&#xff09; 2.为什么要研究数据结构? ? (Why) 3.如何更好的研究数据结构? ? &#xff1f;(How) 注&#xff1a;特别…

Hadoop小结

Hadoop是什么Hadoop是一 个由Apache基金 会所开发的分布式系统基础架构。主要解决,海量数据的存储和海量数据的分析计算问题。广义上来说&#xff0c;Hadoop通 常是指一个更广泛的概念一Hadoop 生态圈。Hadoop优势Hadoop组成HDFS架构Hadoop Distributed File System&#xff0c…

蓝桥杯--ISBN号码

ISBN号码 技巧 数字转为字符【数字‘0’】 字符转为数字【字符-‘0’】 这道题比较简单 题目大意 每一本正式出版的图书都有一个 ISBN 号码与之对应&#xff0c;ISBN 码包括 9 位数字、1 位识别码和 3 位分隔符&#xff0c;其规定格式如 “x-xxx-xxxxx-x”&#xff0c;其中符号…

java多线程(二四)java多线程基础总结

一、进程与线程 1.进程 进程是操作系统结构的基础&#xff1b;是一次程序的执行&#xff1b;是一个程序及其数据在处理机上顺序执行时所发生的活动。操作系统中&#xff0c;几乎所有运行中的任务对应一条进程&#xff08;Process&#xff09;。一个程序进入内存运行&#xff…

前装L2标配车型均价连续第二年「低于」L1,市场进入爆发期

L2级辅助驾驶&#xff0c;正在进入市场红利期。 高工智能汽车研究院监测数据显示&#xff0c;2022年度中国市场&#xff08;不含进出口&#xff09;乘用车前装标配搭载辅助驾驶&#xff08;L0-L2&#xff09;交付1001.22万辆&#xff0c;首次突破千万辆规模&#xff0c;同时&a…

带你玩转spring声明式事务-使用中需要注意的点

本文向大家介绍spring声明式事务使用过程中需要注意的地方。事务特性1. 原子性&#xff08;Atomicity&#xff09;事务是一个原子操作&#xff0c;由一系列动作组成。事务的原子性确保动作要么全部完成&#xff0c;要么完全不起作用。2. 一致性&#xff08;Consistency&#xf…

九龙证券|6G概念重新活跃 数字经济板块引领A股尾盘回升

周三&#xff0c;沪深两市缩量调整&#xff0c;沪指全天以弱势震荡为主&#xff0c;尾盘在数字经济概念带动下快速拉升&#xff0c;全天微跌0.06%&#xff0c;报3283.25点&#xff1b;深证成指跌落0.09%&#xff0c;报15598.29点&#xff1b;创业板指跌落0.26%&#xff0c;报23…

[算法]归并排序

参考&#xff1a;《漫画算法-小灰的算法之旅》 目录 参考&#xff1a;《漫画算法-小灰的算法之旅》 1、什么是归并排序 2、归并的具体操作 3、代码 4、时间复杂度和空间复杂度 5、归并排序是稳定排序 1、什么是归并排序 归并排序就像是组织一场元素之间的“比武大会”&…