[전문가를 위한 C++] 기타 라이브러리 유틸리티
‘전문가를 위한 C++ - Marc Gregoire 지음, 남기혁 옮김’ 책을 참고하여 작성한 포스트입니다.
어휘타입(vocabulary type)
- variant와 any 를 알아보자
variant
- 주어진 집합 타입 중 어느 한 타입이 값을 가진다.
<variant>
에 정의되어 있다.- 타입들을 반드시 지정해 주어야 한다.
- 첫번째 타입을 디폴트 타입으로 설정하는데, 첫번째 타입이 디폴트 생성을 지원해야 한다.
- 타입들이 모두 디폴트 생성을 지원하지 않지만 디폴트 생성을 하고 싶다면 첫 번째 타입을 std::monostate로 지정한다.
- variant 는 값을 하나만 가진다. 여럿이 아님
- index() 를 통해 현재 저장된 값의 타입에 대한 인덱스를 알 수 있다.
- std::holds_alternative() 함수 템플릿을 이용하면 variant가 인수로 지정한 타입의 값을 담고 있는 지 알 수 있다.
- std::get
() 나 std::get () 를 이용하면 variant에 담긴 값을 가져올 수 있다. - index는 검색할 유형의 0 기반 인덱스고, T는 검색할 유형이다.
- 잘못된 값의 타입이나 인덱스를 지정하면 bad_variant_access 예외를 던지는데, get_if
() 나 get_if () 헬퍼 함수를 이용하면 예외가 발생하지 않는다. variant에 대한 포인터를 인수로 받아 요청한 값에 대한 포인터를 리턴한다. - std::visit() 헬퍼 함수는 variant에 대한 방문자 패턴(visitor pattern)을 적용할 때 사용한다. variant에 저장 가능한 모든 타입을 받는 콜러블이어야 한다.
- variant에는 배열을 저장할 수 없다. optional과 같이 레퍼런스를 직접 저장할 수 없기 때문.
예시코드
#include <iostream>
#include <variant>
#include <string>
using namespace std;
class MyVisitor
{
public:
void operator()(int i) { cout<< " int " << i << endl; }
void operator()(const string& s) { cout<< " string " << s << endl; }
void operator()(float f) { cout<< " float " << f << endl; }
};
int main()
{
variant<int, string, float> v;
v = 12;
v = 12.5f;
v = "An std::string"s;
cout<< "Type index: " << v.index() << endl;
cout << "Contains an int: " << holds_alternative<int>(v) << endl;
cout << get<string>(v) << endl;
string* TheString = get_if<string>(&v);
cout << "retrieved string: " << (TheString ? *TheString : "Null") << endl;
visit(MyVisitor(), v);
}
/* 출력 결과
Type index: 1
Contains an int: 0
An std::string
retrieved string: An std::string
string An std::string
*/
any
- 값 하나를 모든 타입으로 저장할 수 있는 클래스
any
에 정의되어 있음- any 생성자나 std::make_any() 헬퍼함수로 생성 가능.
- any_cast() 를 이용해 any에 담긴 값 구할 수 있음
- any에도 레퍼런스를 직접 담을 수 없다.
tuple
<tuple>
에 정의되어 있음- pair를 일반화한 클래스이며, 마찬가지로 크기와 값의 타입이 고정되어 있고, 컴파일 시간에 결정됨
- tuple 생성자로 만든다.
- std::get() 는 i 번째 원소를 가져온다. i의 값이 컴파일시간에 결정되어야 하므로 반복문에서 사용할 수 없다. 하고 싶다면 템플릿 메타프로그래밍으로 구현해야 한다.(26장)
- std::get
() 는 원소의 타입을 지정해 튜플의 원소를 가져온다. 여러개라면 컴파일 에러가 발생한다. - std::tuple_size로 튜플의 크기를 알 수 있다. 이때 tuple의 타입을 지정해 주어야 한다. 잘 모르면 decltype을 이용하자.
- CTAD를 이용하면 tuple 생성 시 템플릿 타입 매개변수를 생략할 수 있다. 이때 레퍼런스를 바로 사용할 수 없어, 쓰고싶다면 ref나 cref를 이용해 주어야 한다.
- std::make_tuple() 유틸리티 함수로 tuple 생성이 가능하다.
Leave a comment