现代C++16 pair

文章目录

      • [1. **概述**](#1. 概述)
      • [2. **成员类型和成员对象**](#2. 成员类型和成员对象)
      • [3. **构造函数**](#3. 构造函数)
      • [4. **成员函数**](#4. 成员函数)
      • [5. **非成员函数**](#5. 非成员函数)
        • [5.1 **`make_pair`**](#5.1 make_pair)
        • [5.2 **比较运算符**](#5.2 比较运算符)
        • [5.3 **`std::swap`**](#5.3 std::swap)
        • [5.4 **`std::get`**](#5.4 std::get)
      • [6. **辅助类**](#6. 辅助类)
        • [6.1 **`std::tuple_size` 和 `std::tuple_element`**](#6.1 std::tuple_sizestd::tuple_element)
        • [6.2 **`std::common_type` 和 `std::basic_common_reference`**](#6.2 std::common_typestd::basic_common_reference)
        • [6.3 **`std::formatter`**](#6.3 std::formatter)
      • [7. **析构函数**](#7. 析构函数)
      • [8. **示例代码**](#8. 示例代码)
      • [9. **总结**](#9. 总结)
      • [1. **`std::pair` 与 `std::tuple` 的集成**](#1. std::pairstd::tuple 的集成)
        • [示例:将 `std::pair` 转换为 `std::tuple`](#示例:将 std::pair 转换为 std::tuple)
        • 输出:
        • [示例:将 `std::tuple` 转换为 `std::pair`](#示例:将 std::tuple 转换为 std::pair)
        • 输出:
      • [2. **`std::pair` 与 `std::map` 的集成**](#2. std::pairstd::map 的集成)
        • [示例:使用 `std::pair` 插入和访问 `std::map` 中的元素](#示例:使用 std::pair 插入和访问 std::map 中的元素)
        • 输出:
        • 解释:
      • [3. **`std::pair` 与 `std::set` 的集成**](#3. std::pairstd::set 的集成)
      • [4. **总结**](#4. 总结)

std::pair 是 C++ 标准库中的一个类模板,用于将两个异构对象组合成一个单元。它在头文件 <utility> 中定义,并且是 std::tuple 的一个特例,专门用于处理两个元素的情况。 std::pair 在许多情况下非常有用,例如作为关联容器(如 std::map)的键值对,或者当需要返回多个值时。

1. 概述

std::pair 的定义如下:

cpp 复制代码
template<class T1, class T2>
struct pair {
    // 成员类型
    typedef T1 first_type;
    typedef T2 second_type;

    // 成员对象
    T1 first;
    T2 second;

    // 构造函数
    constexpr pair() : first(T1()), second(T2()) {}
    constexpr pair(const T1& x, const T2& y) : first(x), second(y) {}
    constexpr pair(T1&& x, T2&& y) : first(std::move(x)), second(std::move(y)) {}
    template<class U1, class U2>
    constexpr pair(U1&& x, U2&& y) : first(std::forward<U1>(x)), second(std::forward<U2>(y)) {}
    // 其他构造函数...

    // 成员函数
    constexpr void swap(pair& other) noexcept(/* see below */);
    // ...
};

2. 成员类型和成员对象

  • first_type :第一个元素的类型,即 T1
  • second_type :第二个元素的类型,即 T2
  • first:存储第一个元素的成员变量。
  • second:存储第二个元素的成员变量。

3. 构造函数

std::pair 提供了多种构造方式:

  • 默认构造 :使用默认构造函数初始化 firstsecond
  • 复制构造 :从两个已有的对象构造 pair
  • 移动构造 :从两个右值引用的对象构造 pair
  • 模板构造 :允许从不同类型的对象构造 pair,支持隐式转换。

4. 成员函数

  • operator= :赋值操作符,用于将另一个 pair 的内容赋值给当前 pair
  • swap :交换两个 pair 的内容。自 C++11 起,swap 函数被标记为 noexcept,如果 T1T2swap 操作也是 noexcept 的话。

5. 非成员函数

5.1 make_pair

make_pair 是一个方便的函数模板,用于创建 std::pair 对象。它会根据传入的参数类型自动推导出 pair 的类型。

cpp 复制代码
template<class T1, class T2>
constexpr pair<T1, T2> make_pair(T1 t, T2 u);

示例:

cpp 复制代码
#include <utility>

int main() {
    auto p = std::make_pair(42, "Hello");
    std::cout << p.first << ", " << p.second << '\n';
}

输出:

42, Hello
5.2 比较运算符

std::pair 支持按字典顺序进行比较。C++20 之前,提供了以下比较运算符:

  • operator==:检查两个 pair 是否相等。
  • operator!=:检查两个 pair 是否不相等。
  • operator<:按字典顺序比较两个 pair
  • operator<=:按字典顺序检查一个 pair 是否小于或等于另一个 pair
  • operator>:按字典顺序检查一个 pair 是否大于另一个 pair
  • operator>=:按字典顺序检查一个 pair 是否大于或等于另一个 pair

C++20 引入了三路比较运算符 operator<=>,它可以在支持三路比较的类型上使用。

cpp 复制代码
template<class T1, class T2>
constexpr std::strong_ordering operator<=>(const pair<T1, T2>& lhs, const pair<T1, T2>& rhs);
5.3 std::swap

std::swap 专门化用于交换两个 pair 的内容。自 C++11 起,std::swap 可以更高效地交换 pair 的元素。

cpp 复制代码
template<class T1, class T2>
void swap(pair<T1, T2>& x, pair<T1, T2>& y) noexcept(/* see below */);
5.4 std::get

std::get 是 C++11 引入的一个模板函数,用于访问 pair 的元素。它可以与 std::tuple 一起使用,但在 pair 上也可以工作。

cpp 复制代码
template<std::size_t I, class T1, class T2>
constexpr typename std::tuple_element<I, pair<T1, T2>>::type& get(pair<T1, T2>& p);

template<std::size_t I, class T1, class T2>
constexpr typename std::tuple_element<I, pair<T1, T2>>::type&& get(pair<T1, T2>&& p);

template<std::size_t I, class T1, class T2>
constexpr const typename std::tuple_element<I, pair<T1, T2>>::type& get(const pair<T1, T2>& p);

示例:

cpp 复制代码
#include <utility>
#include <iostream>

int main() {
    std::pair<int, double> p(42, 3.14);
    std::cout << std::get<0>(p) << ", " << std::get<1>(p) << '\n';
}

输出:

42, 3.14

6. 辅助类

6.1 std::tuple_sizestd::tuple_element

std::tuple_sizestd::tuple_element 是 C++11 引入的模板类,用于获取 pair 的大小和元素类型。

  • std::tuple_size<std::pair<T1, T2>>:表示 pair 的大小为 2。
  • std::tuple_element<0, std::pair<T1, T2>>:表示 pair 的第一个元素类型为 T1
  • std::tuple_element<1, std::pair<T1, T2>>:表示 pair 的第二个元素类型为 T2
6.2 std::common_typestd::basic_common_reference

C++23 引入了 std::common_typestd::basic_common_reference 的特化,用于确定两个 pair 的公共类型和公共引用类型。

6.3 std::formatter

C++23 引入了 std::formatter 的特化,用于支持 std::pair 的格式化输出。这使得 std::pair 可以与 std::format 等函数一起使用。

7. 析构函数

如果 T1T2 都不可能是具有非平凡析构函数的 cv 限定类类型或数组,则 pair 的析构函数是平凡的。这意味着在这种情况下,pair 的析构不会引入额外的开销。

8. 示例代码

以下是一个完整的示例,展示了 std::pair 的基本用法:

cpp 复制代码
#include <iostream>
#include <utility>
#include <string>

int main() {
    // 创建一个 pair
    std::pair<int, std::string> p(42, "Hello");

    // 访问 pair 的元素
    std::cout << "First: " << p.first << ", Second: " << p.second << '\n';

    // 使用 make_pair 创建 pair
    auto p2 = std::make_pair(3.14, 'A');
    std::cout << "First: " << p2.first << ", Second: " << p2.second << '\n';

    // 比较两个 pair
    std::pair<int, int> p3(1, 2);
    std::pair<int, int> p4(1, 3);
    if (p3 < p4) {
        std::cout << "p3 is less than p4\n";
    }

    // 交换两个 pair
    p3.swap(p4);
    std::cout << "After swap: p3 = (" << p3.first << ", " << p3.second << "), p4 = (" << p4.first << ", " << p4.second << ")\n";

    // 使用 std::get 访问元素
    std::pair<double, char> p5(3.14, 'B');
    std::cout << "First: " << std::get<0>(p5) << ", Second: " << std::get<1>(p5) << '\n';

    return 0;
}

9. 总结

std::pair 是 C++ 标准库中一个非常有用的工具,特别适用于需要将两个不同类型的数据组合在一起的情况。它提供了丰富的构造函数、成员函数和非成员函数,使得操作 pair 变得非常灵活和方便。std::pair 还与其他标准库功能(如 std::tuplestd::mapstd::set 等)无缝集成,进一步扩展了其应用场景。

如果你有更多具体的问题或需要进一步的帮助,请随时提问!

std::pair 是 C++ 标准库中一个非常灵活的工具,它可以与其他标准库功能无缝集成,从而扩展其应用场景。下面我将分别介绍 std::pair 如何与 std::tuplestd::mapstd::set 集成,并提供具体的例子。

1. std::pairstd::tuple 的集成

std::pairstd::tuple 的特例,std::tuple 可以看作是 std::pair 的泛化版本,允许存储任意数量的元素。因此,std::pair 可以很容易地转换为 std::tuple,反之亦然。

示例:将 std::pair 转换为 std::tuple
cpp 复制代码
#include <iostream>
#include <utility> // for std::pair
#include <tuple>   // for std::tuple

int main() {
    // 创建一个 std::pair
    std::pair<int, double> p(42, 3.14);

    // 将 std::pair 转换为 std::tuple
    auto t = std::tuple(p.first, p.second);

    // 访问 tuple 中的元素
    std::cout << "Tuple contains: " 
              << std::get<0>(t) << ", " 
              << std::get<1>(t) << '\n';

    return 0;
}
输出:
Tuple contains: 42, 3.14
示例:将 std::tuple 转换为 std::pair
cpp 复制代码
#include <iostream>
#include <utility> // for std::pair
#include <tuple>   // for std::tuple

int main() {
    // 创建一个 std::tuple
    std::tuple<int, double> t(42, 3.14);

    // 将 std::tuple 转换为 std::pair
    auto p = std::make_pair(std::get<0>(t), std::get<1>(t));

    // 访问 pair 中的元素
    std::cout << "Pair contains: " 
              << p.first << ", " 
              << p.second << '\n';

    return 0;
}
输出:
Pair contains: 42, 3.14

2. std::pairstd::map 的集成

std::map 是一个关联容器,用于存储键值对。std::pair 经常用于表示这些键值对。实际上,std::map 的插入操作返回的是一个 std::pair,其中包含插入的迭代器和一个布尔值,表示插入是否成功。

示例:使用 std::pair 插入和访问 std::map 中的元素
cpp 复制代码
#include <iostream>
#include <map>
#include <utility> // for std::pair

int main() {
    // 创建一个 std::map
    std::map<std::string, int> student_grades;

    // 使用 std::pair 插入元素
    student_grades.insert(std::make_pair("Alice", 95));
    student_grades.insert(std::make_pair("Bob", 88));
    student_grades.insert(std::make_pair("Charlie", 92));

    // 访问 map 中的元素
    for (const auto& [name, grade] : student_grades) {
        std::cout << name << ": " << grade << '\n';
    }

    // 插入并检查是否成功
    auto result = student_grades.insert(std::make_pair("Alice", 100));
    if (!result.second) {
        std::cout << "Insertion failed: Key 'Alice' already exists.\n";
    }

    return 0;
}
输出:
Alice: 95
Bob: 88
Charlie: 92
Insertion failed: Key 'Alice' already exists.
解释:
  • student_grades.insert(std::make_pair("Alice", 95)) 插入了一个键值对,其中 "Alice" 是键,95 是值。
  • auto result = student_grades.insert(std::make_pair("Alice", 100)) 返回一个 std::pair,其中第一个元素是插入的迭代器,第二个元素是一个布尔值,表示插入是否成功。由于 "Alice" 已经存在于 map 中,插入失败,result.secondfalse

3. std::pairstd::set 的集成

std::set 是一个有序集合,存储唯一的元素。虽然 std::set 通常用于存储单个类型的元素,但它也可以存储 std::pair,并且可以基于 pair 的两个元素进行排序。

示例:使用 std::pair 存储多个属性的唯一组合
cpp 复制代码
#include <iostream>
#include <set>
#include <utility> // for std::pair

int main() {
    // 创建一个 std::set,存储 std::pair
    std::set<std::pair<int, std::string>> unique_pairs;

    // 插入一些 pair
    unique_pairs.insert(std::make_pair(1, "Apple"));
    unique_pairs.insert(std::make_pair(2, "Banana"));
    unique_pairs.insert(std::make_pair(3, "Cherry"));

    // 插入重复的 pair(不会插入)
    unique_pairs.insert(std::make_pair(1, "Apple"));

    // 遍历 set 并输出元素
    for (const auto& [number, fruit] : unique_pairs) {
        std::cout << number << ": " << fruit << '\n';
    }

    return 0;
}
输出:
1: Apple
2: Banana
3: Cherry
解释:
  • std::set 会自动根据 pair 的字典顺序进行排序。首先比较 first 元素,如果相同则比较 second 元素。
  • 由于 std::set 只存储唯一的元素,尝试插入相同的 pair(如 std::make_pair(1, "Apple"))不会改变集合的内容。
自定义排序规则

你还可以通过提供自定义的比较函数来控制 std::setstd::pair 的排序方式。例如,如果你想只根据 pairsecond 元素进行排序:

cpp 复制代码
#include <iostream>
#include <set>
#include <utility> // for std::pair

// 自定义比较函数,只比较 second 元素
struct CompareSecond {
    bool operator()(const std::pair<int, std::string>& a, const std::pair<int, std::string>& b) const {
        return a.second < b.second;
    }
};

int main() {
    // 创建一个 std::set,使用自定义比较函数
    std::set<std::pair<int, std::string>, CompareSecond> unique_pairs;

    // 插入一些 pair
    unique_pairs.insert(std::make_pair(1, "Apple"));
    unique_pairs.insert(std::make_pair(2, "Banana"));
    unique_pairs.insert(std::make_pair(3, "Cherry"));

    // 遍历 set 并输出元素
    for (const auto& [number, fruit] : unique_pairs) {
        std::cout << number << ": " << fruit << '\n';
    }

    return 0;
}
输出:
1: Apple
2: Banana
3: Cherry
解释:
  • 在这个例子中,std::set 根据 pairsecond 元素(即字符串)进行排序,而不是默认的字典顺序。

4. 总结

  • std::pairstd::tuplestd::pairstd::tuple 的特例,两者可以轻松互转,适用于需要处理两个元素的情况。
  • std::pairstd::mapstd::map 内部使用 std::pair 来存储键值对,insert 操作返回的也是一个 std::pair,方便检查插入是否成功。
  • std::pairstd::setstd::set 可以存储 std::pair,并且可以根据 pair 的两个元素进行排序,甚至可以通过自定义比较函数来控制排序规则。

通过这些集成,std::pair 成为了 C++ 标准库中非常强大且灵活的工具,适用于多种场景。如果你有更多具体的问题或需要进一步的帮助,请随时提问!

相关推荐
dengjiayue3 分钟前
golang实现简单的reids服务2
开发语言·golang
西岭千秋雪_5 分钟前
设计模式の单例&工厂&原型模式
java·单例模式·设计模式·简单工厂模式·工厂方法模式·抽象工厂模式·原型模式
fanchael_kui18 分钟前
使用elasticsearch-java客户端API生成DSL语句
java·大数据·elasticsearch
m0_7482565621 分钟前
[CTF夺旗赛] CTFshow Web1-14 详细过程保姆级教程~
java
疯狂的沙粒27 分钟前
JavaScript 单例模式的创建与应用
开发语言·前端·javascript·vue.js
余额不足1213829 分钟前
C语言基础六:循环结构及面试上机题
c语言·开发语言
T.O.P1130 分钟前
Spring&SpringBoot常用注解
java·spring boot·spring
m0_748256561 小时前
Rust环境安装配置
开发语言·后端·rust
程序猿阿伟1 小时前
《C++巧铸随机森林:开启智能决策新境界》
开发语言·c++·随机森林
O(1)的boot1 小时前
微服务的问题
java·数据库·微服务