2 minute read

‘전문가를 위한 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 생성이 가능하다.

tuple 분리하기

구조적 바인딩

Leave a comment