#43. [모던 C++ STL] span(C++20)
개요
span은 연속된 메모리 시퀀스의 참조를 전달받아 이터레이팅 하는 개체입니다.
C스타일 배열의 경우 전달받을 때 포인터로 변경되어 요소의 갯수 정보가 유실되어, 요소 갯수를 같이 전달받거나, 함수 템플릿을 활용해야 했는데요, span을 이용하면 요소 갯수 유실없이 전달받을 수 있습니다.
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 배열을 포인터로 전달받습니다.
int Sum1(const int* p, int size) {
int result{0};
for (int i{0}; i < size; ++i) {
result += p[i];
}
return result;
}
// 배열 참조자로 전달받습니다.
// const int (&arr)[]를 사용하면 sizeof(arr)시 크기를 알 수 없으므로 size를 전달받습니다.
int Sum2(const int (&arr)[], int size) {
int result{0};
for (int i{0}; i < size; ++i) {
result += arr[i];
}
return result;
}
// 템플릿으로 배열 참조자를 전달받습니다. 이때 배열 요소 갯수인 N을 추론할 수 있습니다.
template<typename T, size_t N>
int Sum3(const T (&arr)[N]) {
int result{0};
for (int i{0}; i < N; ++i) {
result += arr[i];
}
return result;
}
// 배열을 span으로 전달받습니다.
int Sum4(std::span<int> span) {
int result{0};
auto itr{span.begin()};
auto endItr{span.end()};
for (; itr != endItr; ++itr) {
result += *itr;
}
return result;
}
int arr[]{1, 2, 3};
EXPECT_TRUE(Sum1(arr, sizeof(arr) / sizeof(arr[0])) == 1 + 2 + 3); // 요소 갯수를 전달해야 합니다.
EXPECT_TRUE(Sum2(arr, sizeof(arr) / sizeof(arr[0])) == 1 + 2 + 3); // 요소 갯수를 전달해야 합니다.
EXPECT_TRUE(Sum3(arr) == 1 + 2 + 3); // 함수 템플릿을 사용하면, 요소 갯수를 전달할 필요가 없습니다.
EXPECT_TRUE(Sum4(arr) == 1 + 2 + 3); // span을 사용하면, 요소 갯수를 전달할 필요가 없습니다.
범위(Range)를 이터레이팅하는 뷰(View)와 비슷하지만, span은 뷰(View)와 달리 원본을 수정할 수 있습니다.
1
2
3
4
5
6
std::vector<int> v{1, 2, 3};
std::span<int> span{v};
span[0] = 10;
EXPECT_TRUE(v[0] == 10); // 원본이 수정됩니다.
span 초기화
span은 C스타일 배열, array, vector, 메모리, string 등 연속된 메모리 시퀀스인 개체로 초기화 할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int arr1[]{1, 2, 3};
std::span<int> a{arr1}; // C스타일 배열
std::array<int, 3> arr2{1, 2, 3};
std::span<int> b{arr2}; // C++ array
std::vector<int> v1{1, 2, 3};
std::span<int> c{v1}; // vector
std::vector<int> v2{1, 2, 3, 4, 5};
std::span<int> d{v2.data(), 3}; // 특정 메모리와 갯수
EXPECT_TRUE(
std::ranges::equal(a, b) && // 요소들이 같으면 true입니다.
std::ranges::equal(b, c) &&
std::ranges::equal(c, d)
);
std::string str{"abc"};
std::span<char> e{str}; // string
EXPECT_TRUE(e[0] == 'a' && e[1] == 'b' && e[2] == 'c');
또한 생성시 크기를 전달하여 size()
를 컴파일 타임 함수로 사용할 수 있습니다.
1
2
3
4
5
6
std::vector<int> v{1, 2, 3};
std::span<int> dynamicSpan{v}; // 런타임에 크기가 결정됩니다. 동적 크기 span
std::span<int, 3> staticSpan{v}; // 컴파일 타임에 크기가 결정됩니다. 정적 크기 span
// static_assert(dynamicSpan.size() == 3); (X) 컴파일 오류. 컴파일 타임 함수가 아닙니다.
static_assert(staticSpan.size() == 3); // (O) 컴파일 타임 함수입니다.
span 멤버 함수
항목 | 내용 |
---|---|
operator =() (C++20~) |
얕은 복사를 합니다. |
begin(), end() (C++20~) |
순방향 이터레이터를 리턴합니다. |
rbegin(), rend() (C++20~) |
역방향 이터레이터를 리턴합니다. |
cbegin(), cend() (C++23~) |
순방향 이터레이터를 리턴합니다. 이때 요소를 수정할 수 없습니다. |
crbegin(), crend() (C++23~) |
역방향 이터레이터를 리턴합니다. 이때 요소를 수정할 수 없습니다. |
front() (C++20~) |
첫번째 요소의 참조자를 리턴합니다. |
back() (C++20~) |
마지막 요소의 참조자를 리턴합니다. |
at(position) (C++20~) |
position 위치의 요소 참조자를 리턴합니다. position 이 잘못된 위치이면 [] 과 달리 예외를 발생시키며, 검사 코드가 추가되어 상대적으로 속도 부하가 있습니다. |
operator []() (C++20~) |
position 위치의 요소 참조자를 리턴합니다. position 이 잘못된 위치라면 아무 생각없이 실행되어 오동작 합니다. |
data() (C++20~) |
참조하는 개체가 관리하는 메모리 블록을 리턴합니다.(첫번째 요소의 포인터를 리턴합니다.) |
size() (C++20~) |
요소 갯수를 리턴합니다. |
size_bytes() (C++20~) |
바이트 수를 리턴합니다. |
empty() (C++20~) |
비었는지 확인합니다. |
first() (C++20~) |
처음부터 N 개의 요소로 구성되는 subspan 을 구합니다. |
last() (C++20~) |
끝에서부터 N 개의 요소로 구성되는 subspan 을 구합니다. |
subspan(offset, count) (C++20~) |
offset 부터 count 개의 subspan 을 구합니다. |
as_bytes(), as_writable_bytes() (C++20~) |
(작성중) |
dynamic_extent (C++20~) |
(작성중) |
댓글남기기