3 분 소요

개요

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 초기화

spanC스타일 배열, 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~) (작성중)

태그:

카테고리:

업데이트:

댓글남기기