C++17 Structured Bindings 완벽 가이드
C++17의 Structured Bindings(구조적 바인딩)는 배열, 구조체, 튜플, pair 등 복합 타입의 요소들을 개별 변수에 한 번에 바인딩할 수 있게 해주는 문법입니다. Python의 언패킹과 유사하지만 C++의 타입 시스템 안에서 동작합니다.
1. 기본 문법
섹션 제목: “1. 기본 문법”auto [변수1, 변수2, ...] = 표현식;auto 뒤에 대괄호로 변수명 목록을 지정하면, 우변 표현식의 각 요소가 해당 변수에 바인딩됩니다.
2. 사용 가능한 타입
섹션 제목: “2. 사용 가능한 타입”2.1 배열
섹션 제목: “2.1 배열”#include <iostream>
int arr[3] = {10, 20, 30};auto [a, b, c] = arr; // a=10, b=20, c=30
std::cout << a << ", " << b << ", " << c << "\n"; // 10, 20, 302.2 std::pair
섹션 제목: “2.2 std::pair”#include <map>#include <string>
std::map<std::string, int> scores = {{"Alice", 95}, {"Bob", 87}};
for (const auto& [name, score] : scores) { std::cout << name << ": " << score << "\n";}// Alice: 95// Bob: 872.3 std::tuple
섹션 제목: “2.3 std::tuple”#include <tuple>
auto getData() { return std::make_tuple(42, 3.14, std::string("hello"));}
auto [id, ratio, label] = getData();std::cout << id << ", " << ratio << ", " << label << "\n"; // 42, 3.14, hello2.4 사용자 정의 구조체
섹션 제목: “2.4 사용자 정의 구조체”멤버 변수가 public인 경우 자동으로 분해됩니다.
struct Point { double x; double y; double z;};
Point origin{1.0, 2.0, 3.0};auto [px, py, pz] = origin;
std::cout << px << ", " << py << ", " << pz << "\n"; // 1, 2, 33. 참조 바인딩
섹션 제목: “3. 참조 바인딩”auto&로 선언하면 원본 데이터에 대한 참조로 바인딩됩니다.
std::pair<int, std::string> item{1, "apple"};
auto& [id, name] = item;name = "banana"; // item.second도 변경됨
std::cout << item.second << "\n"; // bananaconst auto&는 읽기 전용 참조입니다.
for (const auto& [key, val] : myMap) { // key, val 수정 불가 std::cout << key << ": " << val << "\n";}4. 내부 동작 원리
섹션 제목: “4. 내부 동작 원리”컴파일러는 structured binding을 다음과 같이 변환합니다.
// 원본 코드auto [x, y] = std::make_pair(1, 2);
// 컴파일러가 생성하는 코드 (개념적 표현)auto __e = std::make_pair(1, 2);using __E = decltype(__e);auto& x = std::get<0>(__e);auto& y = std::get<1>(__e);실제로는 각 바인딩 변수가 내부 임시 객체의 멤버에 대한 참조로 처리됩니다. 따라서 임시 객체의 생명주기가 바인딩 변수의 생명주기와 일치합니다.
5. 사용자 정의 타입에 get<> 지원 추가
섹션 제목: “5. 사용자 정의 타입에 get<> 지원 추가”std::get<>과 std::tuple_size, std::tuple_element를 특수화하면 임의의 타입도 structured binding을 지원할 수 있습니다.
#include <tuple>
class Color {public: uint8_t r, g, b; Color(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {}};
// std::tuple_size 특수화template<>struct std::tuple_size<Color> : std::integral_constant<std::size_t, 3> {};
// std::tuple_element 특수화template<std::size_t I>struct std::tuple_element<I, Color> { using type = uint8_t;};
// get<> 함수 구현template<std::size_t I>uint8_t get(const Color& c) { if constexpr (I == 0) return c.r; else if constexpr (I == 1) return c.g; else return c.b;}
// 이제 structured binding 사용 가능Color red{255, 0, 0};auto [r, g, b] = red;std::cout << (int)r << ", " << (int)g << ", " << (int)b << "\n"; // 255, 0, 06. 실전 활용 패턴
섹션 제목: “6. 실전 활용 패턴”6.1 함수에서 다중 반환값
섹션 제목: “6.1 함수에서 다중 반환값”struct ParseResult { bool success; int value; std::string error;};
ParseResult parseInteger(const std::string& s) { try { return {true, std::stoi(s), ""}; } catch (...) { return {false, 0, "Invalid integer: " + s}; }}
auto [ok, val, err] = parseInteger("42");if (ok) { std::cout << "Parsed: " << val << "\n";} else { std::cout << "Error: " << err << "\n";}6.2 map 삽입 결과 처리
섹션 제목: “6.2 map 삽입 결과 처리”std::map<std::string, int> registry;
auto [it, inserted] = registry.emplace("item", 42);if (inserted) { std::cout << "새 항목 삽입: " << it->second << "\n";} else { std::cout << "이미 존재: " << it->second << "\n";}6.3 범위 기반 for + 인덱스
섹션 제목: “6.3 범위 기반 for + 인덱스”#include <ranges>
std::vector<std::string> items = {"alpha", "beta", "gamma"};
for (auto [i, val] : std::views::enumerate(items)) { // C++23 std::cout << i << ": " << val << "\n";}7. 주의사항
섹션 제목: “7. 주의사항”- 바인딩 변수 수는 분해되는 요소 수와 정확히 일치해야 합니다.
auto&&는 universal reference로 동작하여 이동 의미론을 활용할 수 있습니다.- 중첩 structured binding은 지원되지 않습니다(
auto [[a,b], c]불가). - 비트필드에는 structured binding이 적용되지 않습니다.
8. auto 한정자 선택 가이드
섹션 제목: “8. auto 한정자 선택 가이드”std::map<std::string, std::vector<int>> data = { {"scores", {90, 85, 92}}, {"ages", {25, 30, 28}}};
// auto — 값 복사 (데이터 복사 비용 발생)for (auto [key, values] : data){ // key와 values는 복사본 — 원본 변경 안 됨 values.push_back(0); // data에 영향 없음}
// const auto& — 읽기 전용 참조 (가장 일반적)for (const auto& [key, values] : data){ std::cout << key << ": " << values.size() << " items\n"; // values.push_back(0); // 오류: const 참조}
// auto& — 읽기/쓰기 참조 (원본 수정)for (auto& [key, values] : data){ values.push_back(0); // data 내용이 실제로 변경됨}
// auto&& — 유니버설 레퍼런스 (이동 의미론 활용)for (auto&& [key, values] : data){ // 임시 객체라면 이동, lvalue라면 참조로 바인딩}9. C++20/23에서의 확장
섹션 제목: “9. C++20/23에서의 확장”// C++20: structured binding을 람다에서 캡처std::pair<int, std::string> p{42, "hello"};auto [num, str] = p;
// C++20 이전: structured binding 변수를 람다에서 직접 캡처 불가// C++20 이후: 가능auto lambda = [num, str]() { // C++20: 캡처 가능 std::cout << num << " " << str << "\n";};lambda();
// C++23: views::enumerate와 결합#include <ranges>std::vector<std::string> names = {"Alice", "Bob", "Carol"};
for (auto [index, name] : std::views::enumerate(names)){ std::cout << index << ": " << name << "\n";}// 0: Alice// 1: Bob// 2: Carol10. 실전 활용 — 알고리즘 반환값 처리
섹션 제목: “10. 실전 활용 — 알고리즘 반환값 처리”#include <algorithm>#include <vector>#include <string>
// std::minmax_element 반환값 분해std::vector<int> scores = {85, 92, 78, 95, 88};auto [min_it, max_it] = std::minmax_element(scores.begin(), scores.end());std::cout << "최소: " << *min_it << ", 최대: " << *max_it << "\n";
// std::equal_range 반환값 분해std::vector<int> sorted = {1, 2, 2, 3, 3, 3, 4};auto [lower, upper] = std::equal_range(sorted.begin(), sorted.end(), 3);std::cout << "3의 개수: " << std::distance(lower, upper) << "\n"; // 3
// std::mismatch 반환값 분해std::string s1 = "hello world";std::string s2 = "hello C++";auto [it1, it2] = std::mismatch(s1.begin(), s1.end(), s2.begin());std::cout << "첫 차이: '" << *it1 << "' vs '" << *it2 << "'\n";Structured Bindings는 코드의 가독성을 크게 향상시키는 C++17의 핵심 기능입니다. 특히 범위 기반 for 루프에서 map을 순회하거나, 함수에서 여러 값을 반환받을 때 강력한 표현력을 발휘합니다.
| 한정자 | 의미 | 사용 시점 |
|---|---|---|
auto | 값 복사 | 작은 타입, 수정 불필요 |
const auto& | 읽기 전용 참조 | 읽기 전용 순회 (가장 흔함) |
auto& | 읽기/쓰기 참조 | 원본 수정 필요 |
auto&& | 유니버설 참조 | 이동 의미론, 제네릭 코드 |
핵심 규칙:
- 바인딩 변수 수는 분해 요소 수와 정확히 일치해야 함
- 중첩 structured binding(
auto [[a,b], c]) 불가 - 비트필드에는 적용 불가
- C++20부터 람다에서 캡처 가능