본문으로 건너뛰기
Previous
Next
C++ tuple detailed Complete Guide | 'Tuple' guide

C++ tuple detailed Complete Guide | 'Tuple' guide

C++ tuple detailed Complete Guide | 'Tuple' guide

이 글의 핵심

C++ tuple: basic usage, structured binding (C++17), principles, code, and practical applications.

Entering

std::tuple is a type introduced in C++11 that stores multiple values ​​together. This is useful when returning multiple values ​​from a function or temporarily grouping multiple values ​​together.

#include <tuple>
#include <iostream>
#include <string>

int main() {
    // Creating a tuple: Bundles values ​​of multiple types into one
    // <int, double, std::string>: Types to store (order is important)
    std::tuple<int, double, std::string> t{42, 3.14, "hello"};
    
    // Access: std::get<index>(tuple)
    // Index must be a compile-time constant (template argument)
    int x = std::get<0>(t);        // first element (int)
    double y = std::get<1>(t);     // second element (double)
    std::string z = std::get<2>(t); // third element (string)
    
    std::cout << x << ", " << y << ", " << z << std::endl;
    
    return 0;
}

output of power:

42, 3.14, hello

1. Basic use

Create

#include <tuple>
#include <iostream>
#include <string>

int main() {
    // Method 1: Create directly (specify type)
    std::tuple<int, double> t1{42, 3.14};
    
    // Method 2: make_tuple (automatic type inference)
    // "hello" is inferred as const char*
    auto t2 = std::make_tuple(42, 3.14, "hello");
    
    // Method 3: C++17 CTAD (Class Template Argument Deduction)
    // Inferring type from constructor arguments without specifying type
    // std::string("world"): explicitly specify string type
    std::tuple t3{42, 3.14, std::string("world")};  // C++17
    
    // Size: Check the number of elements in a tuple at compile time
    // std::tuple_size_v: Returns the number of elements in a tuple (C++17)
    // decltype(t1): Extract type of t1
    constexpr size_t size1 = std::tuple_size_v<decltype(t1)>;  // 2
    constexpr size_t size2 = std::tuple_size_v<decltype(t2)>;  // 3
    
std::cout << "t1 size: " << size1 << std::endl;
std::cout << "t2 size: " << size2 << std::endl;
    
    return 0;
}

output of power:

t1 size: 2
t2 size: 3

Access

#include <tuple>
#include <iostream>
#include <string>

int main() {
    std::tuple<int, double, std::string> t{42, 3.14, "hello"};
    
    // access by index
    int x = std::get<0>(t);
    double y = std::get<1>(t);
    std::string z = std::get<2>(t);
    
    std::cout << "x: " << x << std::endl;
    std::cout << "y: " << y << std::endl;
    std::cout << "z: " << z << std::endl;
    
    // Access by type (when the type is unique)
    int x2 = std::get<int>(t);
    double y2 = std::get<double>(t);
    std::string z2 = std::get<std::string>(t);
    
    std::cout << "x2: " << x2 << std::endl;
    
    return 0;
}

output of power:

x: 42
y: 3.14
z: hello
x2: 42

2. structured binding (C++17)

#include <tuple>
#include <iostream>
#include <string>

std::tuple<int, std::string, bool> parseUser(const std::string& data) {
    // Parsing logic (example)
    int id = 123;
    std::string name = "Alice";
    bool active = true;
    
    // Return tuple: Return multiple values ​​at once
    // {id, name, active}: Create tuple with curly brace initialization
    return {id, name, active};
}

int main() {
    // C++17 structured binding: decomposing tuple into individual variables
    // auto: Automatic type inference (tuple<int, string, bool>)
    // [id, name, active]: Variable name to receive each element
    // Elements of tuple are assigned in order
    auto [id, name, active] = parseUser("data");
    
    std::cout << "ID: " << id << std::endl;
std::cout << "name: " << name << std::endl;
std::cout << "active: " << (active ? "true" : "false") << std::endl;
    
    return 0;
}

output of power:

ID: 123
Name: Alice
active: true

3. tie

Unpack to existing variable

#include <tuple>
#include <iostream>

std::tuple<int, double> getValues() {
    return {42, 3.14};
}

int main() {
    // Declare an existing variable (uninitialized)
    int x;
    double y;
    
    // std::tie: Create a tuple by binding existing variables by reference.
    // The return value of getValues() is assigned to x and y, respectively.
    // Unlike structured binding, reuse existing variables
    std::tie(x, y) = getValues();
    
    std::cout << "x: " << x << std::endl;
    std::cout << "y: " << y << std::endl;
    
    // std::ignore: Used when you want to ignore a specific element.
    // Discard the second element (double) and only accept the first.
    std::tie(x, std::ignore) = getValues();
std::cout << "x (update): " << x << std::endl;
    
    return 0;
}

output of power:

x: 42
y: 3.14
x (updated): 42

Comparison operations

#include <tuple>
#include <iostream>
#include <string>

struct Person {
    std::string name;
    int age;
    double height;
    
    // Compare with tuples
    auto asTuple() const {
        return std::tie(name, age, height);
    }
    
    bool operator<(const Person& other) const {
        return asTuple() < other.asTuple();
    }
    
    bool operator==(const Person& other) const {
        return asTuple() == other.asTuple();
    }
};

int main() {
    Person p1{"Alice", 25, 165.5};
    Person p2{"Bob", 30, 175.0};
    Person p3{"Alice", 25, 165.5};
    
    if (p1 < p2) {
        std::cout << "p1 < p2" << std::endl;
    }
    
    if (p1 == p3) {
        std::cout << "p1 == p3" << std::endl;
    }
    
    return 0;
}

output of power:

p1 < p2
p1 == p3

4. Practical example

Example 1: Returning multiple values

#include <tuple>
#include <iostream>
#include <string>
#include <cmath>

std::tuple<double, double, double> solveQuadratic(double a, double b, double c) {
    double discriminant = b * b - 4 * a * c;
    
    if (discriminant < 0) {
        return {NAN, NAN, discriminant};
    }
    
    double sqrtD = std::sqrt(discriminant);
    double x1 = (-b + sqrtD) / (2 * a);
    double x2 = (-b - sqrtD) / (2 * a);
    
    return {x1, x2, discriminant};
}

int main() {
    auto [x1, x2, discriminant] = solveQuadratic(1, -5, 6);
    
    if (std::isnan(x1)) {
std::cout << "No real root (discriminant: " << discriminant << ")" << std::endl;
    } else {
        std::cout << "x1: " << x1 << std::endl;
        std::cout << "x2: " << x2 << std::endl;
std::cout << "Discriminant: " << discriminant << std::endl;
    }
    
    return 0;
}

output of power:

x1: 3
x2: 2
Discriminant: 1

Example 2: Sorting containers

#include <tuple>
#include <vector>
#include <algorithm>
#include <iostream>
#include <string>

int main() {
    std::vector<std::tuple<int, std::string, double>> students = {
        {1, "Alice", 85.5},
        {2, "Bob", 92.0},
        {3, "Charlie", 78.5},
        {4, "David", 92.0},
        {5, "Eve", 85.5}
    };
    
    // Sort descending by score
    std::sort(students.begin(), students.end(), 
         {
            return std::get<2>(a) > std::get<2>(b);
        });
    
std::cout << "=== Score order ===" << std::endl;
    for (const auto& [id, name, score] : students) {
std::cout << name << ": " << score << "point" << std::endl;
    }
    
    // Sort by score, name
    std::sort(students.begin(), students.end(), 
         {
            auto score_a = std::get<2>(a);
            auto score_b = std::get<2>(b);
            if (score_a != score_b) {
                return score_a > score_b;
            }
            return std::get<1>(a) < std::get<1>(b);
        });
    
std::cout << "\n=== Score, name order ===" << std::endl;
    for (const auto& [id, name, score] : students) {
std::cout << name << ": " << score << "point" << std::endl;
    }
    
    return 0;
}

output of power:

Run the following commands:

=== Score order ===
Bob: 92 points
David: 92 points
Alice: 85.5 points
Eve: 85.5 points
Charlie: 78.5 points

=== Score, by name ===
Bob: 92 points
David: 92 points
Alice: 85.5 points
Eve: 85.5 points
Charlie: 78.5 points

Example 3: Map key

#include <tuple>
#include <map>
#include <iostream>
#include <string>

int main() {
    // composite key
    std::map<std::tuple<int, std::string>, double> scores;
    
    scores[{1, "Math"}] = 85.5;
    scores[{1, "English"}] = 92.0;
    scores[{2, "Math"}] = 78.5;
    scores[{2, "English"}] = 88.0;
    
    // check
    auto key = std::make_tuple(1, "Math");
    if (auto it = scores.find(key); it != scores.end()) {
        auto [student_subject, score] = *it;
        auto [id, subject] = student_subject;
std::cout << "student " << id << ", " << subject << ": " << score << "point" << std::endl;
    }
    
    // circuit
std::cout << "\n=== Total score ===" << std::endl;
    for (const auto& [key, score] : scores) {
        auto [id, subject] = key;
std::cout << "student " << id << ", " << subject << ": " << score << "point" << std::endl;
    }
    
    return 0;
}

output of power:

Run the following commands:

Student 1, Math: 85.5 points

=== Total Score ===
Student 1, English: 92 points
Student 1, Math: 85.5 points
Student 2, English: 88 points
Student 2, Math: 78.5 points

5. Tuple operation

comparison

#include <tuple>
#include <iostream>

int main() {
    std::tuple<int, double> t1{42, 3.14};
    std::tuple<int, double> t2{42, 3.14};
    std::tuple<int, double> t3{42, 2.71};
    
    // comparison
    std::cout << "t1 == t2: " << (t1 == t2) << std::endl;  // 1
    std::cout << "t1 == t3: " << (t1 == t3) << std::endl;  // 0
    std::cout << "t1 < t3: " << (t1 < t3) << std::endl;    // 0
    std::cout << "t3 < t1: " << (t3 < t1) << std::endl;    // 1
    
    return 0;
}

output of power:

t1 == t2: 1
t1 == t3: 0
t1 < t3: 0
t3 < t1: 1

connect(tuple_cat)

#include <tuple>
#include <iostream>
#include <string>

int main() {
    std::tuple<int, double> t1{42, 3.14};
    std::tuple<std::string, bool> t2{"hello", true};
    
    // connection
    auto t3 = std::tuple_cat(t1, t2);
    // std::tuple<int, double, std::string, bool>
    
    auto [x, y, z, w] = t3;
    std::cout << x << ", " << y << ", " << z << ", " << w << std::endl;
    
    return 0;
}

output of power:

42, 3.14, hello, 1

6. Frequently occurring problems

Problem 1: Index

#include <tuple>
#include <iostream>

int main() {
    std::tuple<int, double, std::string> t{42, 3.14, "hello"};
    
    // ❌ Runtime index
    // int i = 0;
    // auto x = std::get<i>(t);  // error: i is not constexpr
    
    // ✅ Compile-time index
    auto x = std::get<0>(t);
    std::cout << "x: " << x << std::endl;
    
    return 0;
}

Issue 2: References

#include <tuple>
#include <iostream>

int main() {
    int x = 42;
    double y = 3.14;
    
    // ❌ Copy
    auto t1 = std::make_tuple(x, y);
    std::get<0>(t1) = 100;
    std::cout << "x: " << x << std::endl;  // 42 (unchanged)
    
    // ✅ See also
    auto t2 = std::tie(x, y);
    std::get<0>(t2) = 100;
    std::cout << "x: " << x << std::endl;  // 100 (changed)
    
    // ✅ forward_as_tuple
    auto t3 = std::forward_as_tuple(x, y);
    std::get<0>(t3) = 200;
    std::cout << "x: " << x << std::endl;  // 200
    
    return 0;
}

output of power:

x: 42
x: 100
x: 200

Issue 3: Size

#include <tuple>
#include <iostream>

struct Point {
    int x, y;
};

int main() {
    // tuples have overhead
    std::tuple<int, int> t;
std::cout << "tuple size: " << sizeof(t) << std::endl;  // 8
    
    // struct has no overhead
    Point p;
std::cout << "struct size: " << sizeof(p) << std::endl;  // 8
    
    // complex tuple
    std::tuple<int, double, std::string> t2;
std::cout << "Complex tuple size: " << sizeof(t2) << std::endl;  // 48
    
    return 0;
}

output of power:

tuple size: 8
struct size: 8
Complex tuple size: 48

7. pair vs tuple

comparison

#include <tuple>
#include <utility>
#include <iostream>

int main() {
    // pair: 2
    std::pair<int, double> p{42, 3.14};
    std::cout << "pair: " << p.first << ", " << p.second << std::endl;
    
    // tuple: multiple
    std::tuple<int, double, std::string> t{42, 3.14, "hello"};
    std::cout << "tuple: " << std::get<0>(t) << ", " 
              << std::get<1>(t) << ", " 
              << std::get<2>(t) << std::endl;
    
    return 0;
}

output of power:

pair: 42, 3.14
tuple: 42, 3.14, hello

Selection Guide

Featurespairtuple
Number of Elements2Multiple
Access.first, .secondstd::get<N>
ReadabilityHighlow
Usekey-value, coordinatesreturn multiple values ​​
// ✅ pair: When there are two
std::pair<int, std::string> getUserInfo() {
    return {123, "Alice"};
}

// ✅ tuple: 3 or more
std::tuple<int, std::string, int, double> getUserDetails() {
    return {123, "Alice", 25, 165.5};
}

// ✅ struct: Named field (recommended)
struct User {
    int id;
    std::string name;
    int age;
    double height;
};

8. Practical example: database results

#include <tuple>
#include <vector>
#include <iostream>
#include <string>

using Row = std::tuple<int, std::string, int, double>;

std::vector<Row> queryDatabase() {
    return {
        {1, "Alice", 25, 85.5},
        {2, "Bob", 30, 92.0},
        {3, "Charlie", 28, 78.5}
    };
}

void printResults(const std::vector<Row>& results) {
    std::cout << "ID\tName\tAge\tScore" << std::endl;
    std::cout << "---\t----\t---\t-----" << std::endl;
    
    for (const auto& [id, name, age, score] : results) {
        std::cout << id << "\t" << name << "\t" << age << "\t" << score << std::endl;
    }
}

double calculateAverage(const std::vector<Row>& results) {
    double total = 0;
    for (const auto& [id, name, age, score] : results) {
        total += score;
    }
    return total / results.size();
}

int main() {
    auto results = queryDatabase();
    
    printResults(results);
    
std::cout << "\nAverage score: " << calculateAverage(results) << std::endl;
    
    return 0;
}

output of power:

Run the following commands:

ID	Name	Age	Score
---	----	---	-----
1	Alice	25	85.5
2	Bob	30	92
3	Charlie	28	78.5

Average score: 85.3333

organize

Key takeaways

  1. tuple: Grouping multiple values ​​(C++11)
  2. Access: std::get<N>, structured binding
  3. tie: Unpack to existing variable
  4. Compare: Lexicographic comparison
  5. Practical: Returning multiple values, temporary grouping

pair vs tuple

Featurespairtuple
Number of Elements2Multiple
Access.first, .secondstd::get<N>
ReadabilityHighlow
SizeSmallLarge
Usekey-value, coordinatesreturn multiple values ​​

Practical tips

Principle of use:

  • Returns multiple values
  • Temporary grouping
  • Comparison operations
  • Map composite key

Performance:

  • Stack Allocation
  • With overhead (padding)
  • struct recommended for small cases
  • Compile time size

caution:

  • Index is constexpr
  • Reference is tie/forward_as_tuple
  • Readability (consider struct)
  • Size overhead

Next steps


Good article to read together (internal link)

Here’s another article related to this topic.

Practical tips

These are tips that can be applied right away in practice.

Debugging tips

  • If you run into a problem, check the compiler warnings first.
  • Reproduce the problem with a simple test case

Performance Tips

  • Don’t optimize without profiling
  • Set measurable indicators first

Code review tips

  • Check in advance for areas that are frequently pointed out in code reviews.
  • Follow your team’s coding conventions

Practical checklist

This is what you need to check when applying this concept in practice.

Before writing code

  • Is this technique the best way to solve the current problem?
  • Can team members understand and maintain this code?
  • Does it meet the performance requirements?

Writing code

  • Have you resolved all compiler warnings?
  • Have you considered edge cases?
  • Is error handling appropriate?

When reviewing code

  • Is the intent of the code clear?
  • Are there enough test cases?
  • Is it documented?

Use this checklist to reduce mistakes and improve code quality.


Keywords covered in this article (related search terms)

This article will be helpful if you search for C++, tuple, pair, structured-binding, C++11, etc.



자주 묻는 질문 (FAQ)

Q. 이 내용을 실무에서 언제 쓰나요?

A. C++ tuple: basic usage, structured binding (C++17), principles, code, and practical applications. 실전 예제와 코드로 개념부터 활용까지 정… 실무에서는 위 본문의 예제와 선택 가이드를 참고해 적용하면 됩니다.

Q. 선행으로 읽으면 좋은 글은?

A. 각 글 하단의 이전 글 또는 관련 글 링크를 따라가면 순서대로 배울 수 있습니다. C++ 시리즈 목차에서 전체 흐름을 확인할 수 있습니다.

Q. 더 깊이 공부하려면?

A. cppreference와 해당 라이브러리 공식 문서를 참고하세요. 글 말미의 참고 자료 링크도 활용하면 좋습니다.


같이 보면 좋은 글 (내부 링크)

이 주제와 연결되는 다른 글입니다.


이 글에서 다루는 키워드 (관련 검색어)

C++, tuple, pair, structured-binding, C++11 등으로 검색하시면 이 글이 도움이 됩니다.