std::variant
개요
std::variant
는 타입 안전(type-safe) 공용체(union)로, C++17 표준 라이브러리에 추가된 기능입니다. 주어진 여러 타입 중 하나의 값을 가질 수 있으며, 현재 어떤 타입의 값을 저장하고 있는지 항상 기억합니다. 기존 C 스타일 공용체와 달리 타입 정보가 없어 발생할 수 있는 오류를 컴파일 또는 런타임에 방지할 수 있습니다.
C++ 버전별 주요 키워드 도입 시기
- C++17:
std::variant
가 처음 도입되었습니다.
내용 설명
std::variant
는 템플릿 인자로 명시된 타입들 중 하나의 인스턴스만 저장할 수 있는 컨테이너입니다. 예를 들어 std::variant<int, double, std::string>
은 int
, double
, std::string
중 하나의 값만 가질 수 있습니다.
주요 기능은 다음과 같습니다.
- 값 접근
std::get<T>(variant)
: 타입T
로 값에 접근합니다. 만약variant
가T
타입의 값을 가지고 있지 않으면std::bad_variant_access
예외를 던집니다.std::get<I>(variant)
: 인덱스I
로 값에 접근합니다. 타입과 마찬가지로 현재 저장된 값의 인덱스가I
가 아니면 예외가 발생합니다.
- 타입 확인
std::holds_alternative<T>(variant)
:variant
가 현재T
타입의 값을 가지고 있는지 확인하고bool
값을 반환합니다.
- 안전한 값 접근
std::get_if<T>(&variant)
:variant
가T
타입의 값을 가지고 있다면 해당 값에 대한 포인터를, 그렇지 않다면nullptr
를 반환합니다. 예외를 발생시키지 않아 안전합니다.
- 방문자 패턴
std::visit(visitor, variant)
:variant
가 현재 담고 있는 값에 대해visitor
함수(주로 람다)를 호출합니다. 모든 타입을 안전하고 효율적으로 처리할 수 있는 가장 강력한 기능입니다.
예제 코드
#include <iostream>
#include <string>
#include <variant>
int main()
{
// int 타입으로 초기화
std::variant<int, double, std::string> v = 42;
// 1. std::get<T>로 값 접근 (성공)
std::cout << "1. Initial value: " << std::get<int>(v) << '\n';
// 2. std::get<T>로 값 접근 (실패 시 예외 발생)
try
{
std::get<double>(v); // 현재 int를 담고 있으므로 예외 발생
}
catch (const std::bad_variant_access& e)
{
std::cout << "2. Exception caught: " << e.what() << '\n';
}
// 3. std::holds_alternative로 타입 확인 후 접근
v = 42.0;
if (std::holds_alternative<double>(v))
{
std::cout << "3. Value is double: " << std::get<double>(v) << '\n';
}
// 4. std::get_if로 안전하게 값 접근
v = "Hello, variant!";
if (auto p_str = std::get_if<std::string>(&v))
{
std::cout << "4. Value is string: " << *p_str << '\n';
}
else
{
std::cout << "4. Value is not a string.\n";
}
// 5. std::visit로 모든 타입 처리
v = 100;
std::cout << "5. Visiting variant: ";
std::visit([](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, int>)
std::cout << "It's an int with value " << arg << '\n';
else if constexpr (std::is_same_v<T, double>)
std::cout << "It's a double with value " << arg << '\n';
else if constexpr (std::is_same_v<T, std::string>)
std::cout << "It's a string with value \"" << arg << "\"\n";
}, v);
return 0;
}
실행 결과
1. Initial value: 42
2. Exception caught: Unexpected index
3. Value is double: 42
4. Value is string: Hello, variant!
5. Visiting variant: It's an int with value 100
활용팁
- 오류 처리:
std::get
을 사용할 때는try-catch
블록으로 예외를 처리하거나,std::holds_alternative
로 타입을 먼저 확인하는 것이 안전합니다. 하지만 가장 권장되는 방법은 예외를 발생시키지 않는std::get_if
나std::visit
를 사용하는 것입니다. - 상태 표현: 상태 머신(State Machine)에서 각 상태가 서로 다른 데이터를 가질 때
std::variant
를 사용하면 상태와 데이터를 하나의 객체로 깔끔하게 관리할 수 있습니다. std::visit
활용:std::visit
는variant
를 다루는 가장 일반적이고 강력한 방법입니다. 오버로딩된 람다 세트나 함수 객체를 전달하여variant
가 담고 있는 모든 타입에 대한 처리를 간결하게 작성할 수 있습니다.- 빈 상태:
variant
가 비어있는 상태를 표현하고 싶다면 첫 번째 템플릿 인자로std::monostate
를 추가할 수 있습니다.std::variant<std::monostate, int, std::string>
와 같이 사용하면 기본 생성 시monostate
상태가 됩니다.
'개발 > C++ (98,03,11,14,17,20,23)' 카테고리의 다른 글
Modern C++ : std::optional (17) (0) | 2025.09.28 |
---|---|
Modern C++ : Type Traits 2 (11, 14, 17, 20) (0) | 2025.09.27 |
Modern C++ : Variadic Templates and Fold Expressions (11, 17) (0) | 2025.09.26 |
Modern C++ : std::concepts (20) (0) | 2025.09.25 |
Modern C++ : Type Traits 1 (11, 14, 17, 20) (0) | 2025.09.24 |
Modern C++ : 템플릿 특수화 (Template Specialization) (98, 11, 17) (0) | 2025.09.23 |
Modern C++ : 복사 및 이동 의미론 (Copy and Move Semantics) (98, 11) (0) | 2025.09.22 |
Modern C++ : 연산자 오버로딩 (Operator Overloading) (98, 11, 20) (0) | 2025.09.21 |