C++学习笔记-Pair和Tuple

std::pair和std::tuple是C++标准库中用于组合不同类型数据的容器。pair用于存储两个值,tuple可以存储任意数量的值。它们在函数返回多个值、数据结构组合等场景中非常有用。

std::pair的基本使用

 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
#include <iostream>
#include <utility>
#include <string>

using namespace std::string_literals;

std::pair<std::string, int> GetPair()
{
    return std::make_pair("Mark"s, 100);
}

// 也可以直接返回大括号初始化
std::pair<std::string, int> GetPairDirect()
{
    return {"Alice"s, 85};
}

int main()
{
    std::pair<std::string, int> pair = GetPair();
    
    // 获取pair中的数据
    std::cout << "Name: " << pair.first << ", Score: " << pair.second << std::endl;
    
    // 修改pair中的数据
    pair.first = "Bob";
    pair.second = 95;
    std::cout << "Updated: " << pair.first << ", " << pair.second << std::endl;
}

std::tuple的基本使用

 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
#include <tuple>

std::tuple<int, std::string, int> GetTuple()
{
    return std::make_tuple(1, "Jerry"s, 23);
}

// 使用大括号初始化
std::tuple<int, std::string, int> GetTupleDirect()
{
    return {2, "Tom"s, 25};
}

int main()
{
    std::tuple<int, std::string, int> tuple = GetTuple();
    
    // 获取元组中的数据
    int id = std::get<0>(tuple);
    std::string name = std::get<1>(tuple);
    int age = std::get<2>(tuple);
    
    std::cout << "ID: " << id << ", Name: " << name << ", Age: " << age << std::endl;
    
    // 修改元组中的数据
    std::get<0>(tuple) = 10;
    std::get<1>(tuple) = "Updated Name";
    std::get<2>(tuple) = 30;
    
    std::cout << "Updated: " << std::get<0>(tuple) << ", " 
              << std::get<1>(tuple) << ", " << std::get<2>(tuple) << std::endl;
}

结构化绑定(C++17)

 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
#include <iostream>
#include <tuple>
#include <string>

std::pair<std::string, int> GetPersonInfo()
{
    return {"Charlie", 28};
}

std::tuple<int, std::string, double, bool> GetStudentData()
{
    return {12345, "David", 3.8, true};
}

int main()
{
    // C++17结构化绑定 - pair
    auto [name, age] = GetPersonInfo();
    std::cout << "Name: " << name << ", Age: " << age << std::endl;
    
    // C++17结构化绑定 - tuple
    auto [id, studentName, gpa, isActive] = GetStudentData();
    std::cout << "ID: " << id << ", Name: " << studentName 
              << ", GPA: " << gpa << ", Active: " << isActive << std::endl;
    
    // 也可以指定类型
    const auto& [constName, constAge] = GetPersonInfo();
    std::cout << "Const access: " << constName << ", " << constAge << std::endl;
}

pair的实际应用

 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
50
51
52
53
54
55
56
57
58
59
60
61
#include <vector>
#include <algorithm>
#include <map>

// 函数返回多个值
std::pair<bool, std::string> ValidateInput(const std::string& input)
{
    if (input.empty())
        return {false, "Input cannot be empty"};
    
    if (input.length() < 3)
        return {false, "Input must be at least 3 characters"};
    
    return {true, "Input is valid"};
}

// 在容器中使用pair
void PairInContainers()
{
    // 存储坐标点
    std::vector<std::pair<int, int>> points = {
        {0, 0}, {1, 2}, {3, 4}, {5, 6}
    };
    
    // 遍历点
    for (const auto& point : points)
    {
        std::cout << "Point: (" << point.first << ", " << point.second << ")" << std::endl;
    }
    
    // 使用结构化绑定遍历
    for (const auto& [x, y] : points)
    {
        std::cout << "Coordinate: (" << x << ", " << y << ")" << std::endl;
    }
    
    // 排序pair(默认先按first排序,再按second排序)
    std::sort(points.begin(), points.end());
    
    // map的元素就是pair
    std::map<std::string, int> scores = {
        {"Alice", 95}, {"Bob", 87}, {"Charlie", 92}
    };
    
    for (const auto& [name, score] : scores)
    {
        std::cout << name << ": " << score << std::endl;
    }
}

int main()
{
    // 验证输入示例
    auto [isValid, message] = ValidateInput("Hi");
    std::cout << "Valid: " << isValid << ", Message: " << message << std::endl;
    
    auto [isValid2, message2] = ValidateInput("Hello World");
    std::cout << "Valid: " << isValid2 << ", Message: " << message2 << std::endl;
    
    PairInContainers();
}

tuple的实际应用

 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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <tuple>
#include <vector>
#include <string>

// 数据库查询结果
std::tuple<int, std::string, std::string, double> GetEmployeeRecord(int id)
{
    // 模拟数据库查询
    return {id, "John Doe", "Software Engineer", 75000.0};
}

// 数学计算返回多个结果
std::tuple<double, double, double> SolveQuadratic(double a, double b, double c)
{
    double discriminant = b * b - 4 * a * c;
    
    if (discriminant < 0)
        return {0, 0, discriminant}; // 无实数解
    
    double sqrt_discriminant = std::sqrt(discriminant);
    double x1 = (-b + sqrt_discriminant) / (2 * a);
    double x2 = (-b - sqrt_discriminant) / (2 * a);
    
    return {x1, x2, discriminant};
}

// 统计信息
std::tuple<size_t, double, double, double> CalculateStats(const std::vector<double>& data)
{
    if (data.empty())
        return {0, 0.0, 0.0, 0.0};
    
    size_t count = data.size();
    double sum = 0.0;
    double min_val = data[0];
    double max_val = data[0];
    
    for (double value : data)
    {
        sum += value;
        min_val = std::min(min_val, value);
        max_val = std::max(max_val, value);
    }
    
    double average = sum / count;
    return {count, average, min_val, max_val};
}

int main()
{
    // 员工记录示例
    auto [empId, empName, empPosition, empSalary] = GetEmployeeRecord(1001);
    std::cout << "Employee " << empId << ": " << empName 
              << " (" << empPosition << ") - $" << empSalary << std::endl;
    
    // 二次方程求解
    auto [x1, x2, discriminant] = SolveQuadratic(1, -5, 6);
    if (discriminant >= 0)
    {
        std::cout << "Solutions: x1 = " << x1 << ", x2 = " << x2 << std::endl;
    }
    else
    {
        std::cout << "No real solutions" << std::endl;
    }
    
    // 统计信息
    std::vector<double> numbers = {1.5, 2.8, 3.2, 1.9, 4.1, 2.3, 3.7};
    auto [count, avg, minVal, maxVal] = CalculateStats(numbers);
    std::cout << "Stats - Count: " << count << ", Average: " << avg 
              << ", Min: " << minVal << ", Max: " << maxVal << std::endl;
}

高级用法

tuple_size和tuple_element

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

void TupleMetaInfo()
{
    using MyTuple = std::tuple<int, std::string, double>;
    
    // 获取tuple的大小
    constexpr size_t tupleSize = std::tuple_size_v<MyTuple>;
    std::cout << "Tuple size: " << tupleSize << std::endl;
    
    // 获取特定位置的类型
    using FirstType = std::tuple_element_t<0, MyTuple>;
    using SecondType = std::tuple_element_t<1, MyTuple>;
    using ThirdType = std::tuple_element_t<2, MyTuple>;
    
    std::cout << "First type is int: " << std::is_same_v<FirstType, int> << std::endl;
    std::cout << "Second type is string: " << std::is_same_v<SecondType, std::string> << std::endl;
    std::cout << "Third type is double: " << std::is_same_v<ThirdType, double> << std::endl;
}

tie函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void TieExample()
{
    std::tuple<int, std::string, double> data = {42, "Hello", 3.14};
    
    // 使用tie解包到已存在的变量
    int number;
    std::string text;
    double value;
    
    std::tie(number, text, value) = data;
    std::cout << "Unpacked: " << number << ", " << text << ", " << value << std::endl;
    
    // 忽略某些值
    std::tie(number, std::ignore, value) = data;
    std::cout << "Partial unpack: " << number << ", " << value << std::endl;
}

forward_as_tuple

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
template<typename... Args>
void ProcessArgs(Args&&... args)
{
    auto tuple = std::forward_as_tuple(args...);
    std::cout << "Tuple size: " << std::tuple_size_v<decltype(tuple)> << std::endl;
    
    // 可以完美转发参数
    std::cout << "First arg: " << std::get<0>(tuple) << std::endl;
}

int main()
{
    TupleMetaInfo();
    TieExample();
    
    ProcessArgs(42, "test", 3.14);
    
    return 0;
}

自定义pair和tuple

 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
50
51
52
53
// 自定义pair类型
template<typename T1, typename T2>
struct Point
{
    T1 x;
    T2 y;
    
    Point(T1 x, T2 y) : x(x), y(y) {}
    
    void print() const
    {
        std::cout << "Point(" << x << ", " << y << ")" << std::endl;
    }
};

// 自定义tuple类型
struct Person
{
    std::string name;
    int age;
    std::string city;
    
    Person(const std::string& n, int a, const std::string& c)
        : name(n), age(a), city(c) {}
    
    void print() const
    {
        std::cout << "Person: " << name << ", " << age << ", " << city << std::endl;
    }
};

int main()
{
    // 使用自定义类型
    Point<int, int> p1(10, 20);
    p1.print();
    
    Point<double, double> p2(1.5, 2.7);
    p2.print();
    
    Person person("Alice", 30, "New York");
    person.print();
    
    // 与标准库类型比较
    std::pair<int, int> stdPair(10, 20);
    std::tuple<std::string, int, std::string> stdTuple("Alice", 30, "New York");
    
    std::cout << "Standard pair: " << stdPair.first << ", " << stdPair.second << std::endl;
    std::cout << "Standard tuple: " << std::get<0>(stdTuple) << ", " 
              << std::get<1>(stdTuple) << ", " << std::get<2>(stdTuple) << std::endl;
    
    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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <chrono>

void PerformanceComparison()
{
    const int iterations = 1000000;
    
    // 测试pair性能
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i)
    {
        std::pair<int, int> p = std::make_pair(i, i * 2);
        volatile int sum = p.first + p.second; // 防止优化
    }
    auto end = std::chrono::high_resolution_clock::now();
    auto pairTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    // 测试tuple性能
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i)
    {
        std::tuple<int, int> t = std::make_tuple(i, i * 2);
        volatile int sum = std::get<0>(t) + std::get<1>(t); // 防止优化
    }
    end = std::chrono::high_resolution_clock::now();
    auto tupleTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    // 测试结构体性能
    struct SimpleStruct { int a, b; };
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i)
    {
        SimpleStruct s{i, i * 2};
        volatile int sum = s.a + s.b; // 防止优化
    }
    end = std::chrono::high_resolution_clock::now();
    auto structTime = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    
    std::cout << "Performance comparison (" << iterations << " iterations):" << std::endl;
    std::cout << "Pair: " << pairTime.count() << " microseconds" << std::endl;
    std::cout << "Tuple: " << tupleTime.count() << " microseconds" << std::endl;
    std::cout << "Struct: " << structTime.count() << " microseconds" << std::endl;
}

int main()
{
    PerformanceComparison();
    return 0;
}

总结

  1. make_pair和make_tuple:用于创建pair和tuple对象,可以自动推导类型
  2. 访问方式
    • pair使用.first.second
    • tuple使用std::get<index>()
  3. 结构化绑定:C++17提供了更优雅的解包语法
  4. 实际应用:函数返回多个值、容器存储、数据组合等场景
  5. 性能考虑:对于简单的数据组合,自定义结构体可能更高效且更易读
  6. 使用建议:虽然可以使用元组获取元素,但确实比较麻烦,不推荐使用,因为需要std::get<0>这样,容易出错
updatedupdated2025-09-202025-09-20