5 분 소요

  • (C++11~) tuple이 추가되어 다수의 요소를 관리할 수 있는 데이터 전달용 개체를 좀 더 간편하게 만들 수 있습니다.
  • (C++11~) make_tuple(), tie(), forward_as_tuple()이 추가되어 tuple을 쉽게 생성할 수 있습니다.
  • (C++11~) tuple_cat()이 추가되어 두개의 tuple을 합칠 수 있습니다.
  • (C++11~) tuple_size가 추가되어 컴파일 타임에 tuple요소 갯수를 구할 수 있습니다.
  • (C++11~) tuple_element가 추가되어 tuple 각 요소에 대한 타입을 구할 수 있습니다.
  • (C++11~) piecewise_construct가 추가되어 pair의 개체 생성시 tuple의 요소들로 개체 생성자를 호출할 수 있습니다.
  • (C++14~) 타입 기반 get()이 추가되어 타입들이 서로 다르다면 타입으로 데이터에 접근할 수 있습니다.
  • (C++17~) apply()가 추가되어 tuple 형식을 전달받아 함수자를 호출할 수 있습니다.
  • (C++17~) make_from_tuple()이 추가되어 tuple 형식을 전달받아 개체를 생성할 수 있습니다.

개요

기존에는 데이터를 주고받는 가벼운 개체를 만들기 위해 pair를 사용했는데요, pairfirstsecond로 두개의 요소만 관리할 수 있다는 아쉬움이 있었습니다.

C++11 부터 STL 에서는 tuple이 제공되어 다수의 요소를 관리할 수 있는 데이터 전달용 개체를 손쉽게 만들 수 있습니다.

항목 내용
pair firstsecond로 2개의 요소를 관리합니다.
make_pair() pair를 생성합니다.
piecewise_construct (C++11~) pair의 개체 생성시 tuple의 요소들로 개체 생성자를 호출합니다.
tuple (C++11~) 다수의 요소를 관리할 수 있는 데이터 전달용 개체를 손쉽게 만듭니다.
get() (C++11~) tuple에서 주어진 인덱스 위치에 있는 요소의 참조자를 리턴합니다.
(C++14~) 타입으로 찾을 수 있도록 보강되었습니다.
make_tuple (C++11~) tuple을 생성합니다.
tie() (C++11~) 좌측값 참조로 구성된 tuple을 만듭니다.
forward_as_tuple() (C++11~) 함수 인수 전달에 적합하도록, tuple 생성시 좌측값이나 우측값 참조가 변형되지 않도록 합니다.
tuple_cat() (C++11~) 두개의 tuple을 합칩니다.
tuple_size (C++11~) 컴파일 타임에 tuple의 요소 갯수를 구합니다.
tuple_element (C++11~) tuple의 각 요소에 대한 타입을 제공합니다.
ignore() (C++11~) (작성중)
interger_sequence (C++14~) (작성중)
apply() (C++17~) tuple 형식을 전달받아 함수자를 호출합니다.
make_from_tuple() (C++17~) tuple 형식을 전달받아 개체를 생성합니다.
basic_common_reference (C++23~) (작성중)
common_type (C++23~) (작성중)

tuple

다수의 요소를 관리할 수 있는 데이터 전달용 개체를 만듭니다.

  1. tuple 정의시 사용할 타입을 나열하고,
  2. get() 함수에 0, 1, 2등 인덱스를 이용하여 접근합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
std::tuple<int, std::string, char> Func() {
    return std::tuple<int, std::string, char>{10, "Name", 'a'}; // return {10, "Name", 'a'}; 와 동일합니다.
}

// 3개의 데이터로 구성된 tuple 입니다.
std::tuple<int, std::string, char> result{Func()};

// get()함수를 이용하여 순서를 지정하여 데이터에 접근합니다.
EXPECT_TRUE(
    std::get<0>(result) == 10 && 
    std::get<1>(result) == "Name" && 
    std::get<2>(result) == 'a'
);

(C++17~) 구조화된 바인딩이 추가되어 배열, pair, tuple, 클래스등의 내부 요소나 멤버 변수에 쉽게 접근할 수 있습니다.

make_tuple()

make_pair처럼(pair 참고) tuple을 생성합니다.

1
2
3
std::tuple<int, std::string, char> Func() {
    return std::make_tuple(10, "Name", 'a');
}

tie()

좌측값 참조로 구성된 tuple을 만듭니다. tuple에서 get()함수를 이용해서 데이터를 확인하는데, 참 직관적이지 못합니다. 이럴때 다음처럼 tie() 함수를 이용하여 지역 변수참조자로 구성된 tuple을 만들면 좀더 직관적으로 데이터를 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::tuple<int, std::string, char> Func() {
    return std::make_tuple(10, "Name", 'a');
}

int val;
std::string name;
char ch;

// tie 함수로 지역 변수의 참조자로 구성된 tuple을 만듭니다.
std::tie(val, name, ch) = Func();

EXPECT_TRUE(
    val == 10 && 
    name == "Name" && 
    ch == 'a'
); 

forward_as_tuple()

함수 인수 전달에 적합하도록, tuple 생성시 좌측값이나 우측값 참조가 변형되지 않도록 합니다. forward() 함수 처럼요.

tuple_cat()

두개의 tuple을 합칩니다.

1
2
3
4
5
6
7
8
9
10
11
12
auto t1{std::make_tuple(10, "Name", 'a')};
auto t2{std::make_tuple("Hello", 20, 'b')};

auto t3{std::tuple_cat(t1, t2)};   
EXPECT_TRUE(
    std::get<0>(t3) == 10 && 
    std::get<1>(t3) == "Name" && 
    std::get<2>(t3) == 'a' &&
    std::get<3>(t3) == "Hello" && 
    std::get<4>(t3) == 20 && 
    std::get<5>(t3) == 'b'
); 

tuple_size

컴파일 타임에 tuple의 요소 갯수를 구합니다.

1
static_assert(std::tuple_size<std::tuple<int, std::string, char>>::value == 3);

tuple_element

tuple의 각 요소에 대한 타입을 제공합니다.

1
2
3
4
5
6
7
8
9
10
11
12
using MyTuple = std::tuple<int, std::string, char>;
MyTuple data{10, "Name", 'a'};

std::tuple_element<0, MyTuple>::type intVal{std::get<0>(data)};
std::tuple_element<1, MyTuple>::type stringVal{std::get<1>(data)};
std::tuple_element<2, MyTuple>::type charVal{std::get<2>(data)};

EXPECT_TRUE(
    intVal == 10 && 
    stringVal == "Name" && 
    charVal == 'a'
);

piecewise_construct

piecewise_constructpair의 생성자의 오버라이드 버전을 호출하기 위한 더미(Dummy) 개체 인데요, pairfirstsecond 개체를 생성할때, 전달된 tuple 개체의 요소들로 초기화 해줍니다.

보통 AB 개체를 관리하는 pair를 만든다면,

1
2
3
4
std::pair<A, B> data{
    A{10, "Name", 'a'}, // A 개체 생성
    B{1, 2, 3, 4} // B 개체 생성
};

와 같이 만들텐데요,

piecewise_construct를 사용한 함수 오버로딩 버전을 실행하면, A 개체를 생성할때 사용할 인수들과 B개체를 생성할때 사용할 인수들을 tuple로 전달해서, pair 내부에서 A, B를 생성하도록 할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class A {
    int m_Val;
    const char* m_Str;
    char m_Ch;
public:
    A(int val, const char* str, char ch) : 
        m_Val(val),
        m_Str(str),
        m_Ch(ch) {}
    int GetVal() const {return m_Val;}
    const char* GetStr() const {return m_Str;}
    char GetCh() const {return m_Ch;}
}; 

class B {
    int m_Val;
public:
    B(int a, int b, int c, int d) : m_Val(a + b + c + d) {}
    int GetVal() const {return m_Val;}
};

std::pair<A, B> data{
    std::piecewise_construct, // A, B 개체 생성시 tuple의 값을 인수로 전달함
    std::forward_as_tuple(10, "Name", 'a'), // A 생성자에 전달할 인수들을 tuple로 전달
    std::forward_as_tuple(1, 2, 3, 4) // B 생성자에 전달할 인수들을 tuple로 전달
};

EXPECT_TRUE(
    data.first.GetVal() == 10 &&
    data.first.GetStr() == "Name" &&
    data.first.GetCh() == 'a' &&
    data.second.GetVal() == 1 + 2 + 3 + 4
);

상기 예에서는 인수들의 참조성이 변하지 않도록 forward_as_tuple()을 사용했는데요, make_tuple()을 사용해도 결과는 같습니다.

하지만, 인자로 전달된 개체를 사용할 때에는 참조성이 변할 수도 있으므로, piecewise_construct를 사용할땐 forward_as_tuple()을 사용하는게 좋습니다.

(C++14~) 타입 기반 get()

C++11 에서는 get() 함수가 인덱스 기반으로 tuple 요소에 접근할 수 있었으나, C++14 부터는 타입들이 서로 다르다면 타입으로 데이터에 접근할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
std::tuple<int, std::string, char> Func() {
    return std::tuple<int, std::string, char>{10, "Name", 'a'}; // return {10, "Name", 'a'}; 와 동일합니다.
}

// 3개의 데이터로 구성된 tuple 입니다.
std::tuple<int, std::string, char> result{Func()};

// 타입들이 서로 다르다면 타입을 이용하여 데이터에 접근할 수 있습니다.
EXPECT_TRUE(
    std::get<int>(result) == 10 && 
    std::get<std::string>(result) == "Name" &&
    std::get<char>(result) == 'a' 
);

(C++17~) apply()

apply()tuple 형식을(arraypair도 가능합니다. get()tuple_size와 호환되면 됩니다.) 전달받아 함수자를 호출하는 유틸리티 함수 입니다.

기존에는 tuple의 데이터를 함수에 전달할때 요소의 전개가 필요했습니다.

1
2
3
4
5
6
7
8
int Sum(int a, int b, int c) {
    return a + b + c;
}

std::tuple<int, int, int> data{1, 2, 3};

// tuple의 각 요소를 전개해야 합니다.
EXPECT_TRUE(Sum(std::get<0>(data), std::get<1>(data), std::get<2>(data)) == 1 + 2 + 3);

apply()를 이용하면, 요소의 전개를 은닉할 수 있습니다.

1
2
3
4
5
6
7
8
int Sum(int a, int b, int c) {
    return a + b + c;
}

std::tuple<int, int, int> data{1, 2, 3};

// tuple의 요소를 전개하지 않아도 됩니다.
EXPECT_TRUE(std::apply(Sum, data) == 1 + 2 + 3);

(C++17~) make_from_tuple

make_from_tuple()tuple 형식을(arraypair도 가능합니다. get()tuple_size와 호환되면 됩니다.) 전달받아 개체를 생성하는 유틸리티 함수 입니다. 이를 이용하면, apply()처럼 요소의 전개를 은닉할 수 있습니다.

1
2
3
4
5
6
7
8
class T {
public:
    T(int a, int b, int c) {}
};

std::tuple<int, int, int> data{1, 2, 3};
// tuple의 요소를 전개하지 않아도 됩니다.
T t{std::make_from_tuple<T>(std::move(data))};

태그:

카테고리:

업데이트:

댓글남기기