본문으로 바로가기

C++17) std::variant

category C++/Modern 2019. 2. 28. 03:21

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0088r3.html

 

C++17은 타입-세이프 한 공용체가 필요했고, 그것을 구현했습니다.
std::variant는 union 과 똑같이 행동하지만, 타입에 안전합니다.
std::variant는 값의 수명을 유지하고 관리합니다.  만약 variant가 값을 보유하고 있는경우, 그 값의 유형은 템플릿 인수 유형중 하나여야 합니다. 

주어진 시간에 variant는 객체 유형중 하나의 값을 보유하거나 보유하지 않습니다.
variant 인스턴스가 T 타입의 값을 보유하면, 이는 T 타입의 값이 variant 저장소 내에 할당됨을 의미합니다.
T 타입을 보유하기 위해 동적 메모리같은 추가 저장소를 사용하진 않습니다. 단순히 Types에 있는 모든 유형에 대해 적절히 정렬된 변형 저장소를 사용합니다.
모든 타입은 참조할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
 
    variant<intdoublestd::string> vunion = 1;    // variant type = int
 
    cout << std::get<int>(vunion) << endl;    //1
 
    vunion = 3.5;    //variant type = double, int 객체는 삭제됨
    cout << std::get<double>(vunion) << endl;    //3.5
    cout << std::get<1>(vunion) << endl;    // 위와 같은 효과
    auto t = std::get<double>(vunion);        // t 에 값을 담음
 
    auto i = std::get<int>(vunion);            // bad_variant_access 예외를 던짐
    auto str = std::get<string>(vunion);    // bad_variant_access 예외를 던짐 
 

 

값 접근을 위한 멤버, 비멤버 함수를 지원합니다.

1
2
3
4
5
6
7
8
9
    std::variant<intdoublestd::string> vunion = "abc"s;
 
    vunion.index();    // 2
 
    std::holds_alternative<int>(vunion);            //false
    std::holds_alternative<std::string>(vunion);    // true
 
    std::get_if<std::string>(&vunion);    // string*
    std::get_if<bool>(&vunion);            // nullptr
 
index() 멤버함수는 현재 variant 의 인덱스를 반환합니다.
std::holds_alternative 비멤버함수는 variant 인스턴스를 받아 그 타입이 유효한지에 대해 bool 값을 반환합니다.
std::get_if 비멤버함수는 variant 인스턴스를 받아, 그 타입이 유효하다면 타입의 포인터형을 반환합니다.
타입이 유효하지않다면 bad_variant_access 예외를 던지는 대신, nullptr을 반환합니다.

 

1
2
3
4
5
6
7
8
9
    std::variant<intdoublestd::string> vunion = "abc"s;
 
    vunion.index();    // 2
 
    std::holds_alternative<int>(vunion);            //false
    std::holds_alternative<std::string>(vunion);    // true
 
    std::get_if<std::string>(&vunion);    // string*
    std::get_if<bool>(&vunion);            // nullptr

 

std::visit 라는 비멤버 함수도 지원한다.
이 함수는 std::variant 인스턴스에 담겨있는 타입에 맞게 operator를 호출해준다.
1
2
template <class Visitor, class... Variants>
  constexpr ReturnType visit(Visitor&& vis, Variants&&... vars);
 

std::visit는 기본적으로 방문자 패턴 ( visitor pattern ) 의 구현을 위해 사용된다.

using var_t = std::variant<intdoublestd::string>;
 
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...)->overloaded<Ts...>;
 
int main() {
    std::vector<var_t> varvec = { 10,3.5,"abc"s };
    for (auto& i : varvec) {
        std::visit([](auto&& args) {
            cout << args << endl;
        }, i);
        //void visitor, 부작용만 일으킴
 
        var_t vs = std::visit([](auto&& arg) -> var_t {
            return arg + arg;
        }, i);
        // 값 반환 방문자, 받는 인자를 통해 새로운 타입으로 반환함
 
        std::visit([](auto&& args) {
            using T = std::decay_t<decltype(args)>;
            if constexpr (std::is_same_v<T, int>)
                cout << "타입 : int" << endl;
            else if constexpr (std::is_same_v<T, double>)
                cout << "타입 : double" << endl;
            else if constexpr (std::is_same_v<T, std::string>)
                cout << "타입 : std::string" << endl;
        }, vs);
        // 타입 매칭 방문자, 각 타입을 다르게 처리함
    }
 
    cout << "new varvec" << endl;
 
    for (auto& i : varvec) {
        std::visit(overloaded {
            [](int arg) { cout << "타입 : int" << endl; },
            [](double arg) { cout << "타입 : double" << endl; },
            [](const std::string& arg) { cout << "타입 : std::string" << endl; }
            }, i);
        // 다른 타입 매칭 방문자, operator() 를 호출함
        // 다중 람다를 할 수 있는 이유는 overloaded가 가변인자 템플릿이기 때문
    }
}
cs

 

'C++ > Modern' 카테고리의 다른 글

C++20) Coroutine ( 코루틴 ) - 1  (0) 2020.08.20
C++20) Designated Initializer ( 지정된 초기화 )  (0) 2020.08.19
C++17) std::any  (0) 2019.02.27
C++17) std::optional  (0) 2019.02.27
C++17) std::string_view  (0) 2019.02.26