C++20 std::span과 C++23 std::mdspan — 비소유 배열 뷰
std::span은 C++20에서 도입된 비소유(non-owning) 연속 메모리 뷰입니다. 배열, std::vector, std::array 등 다양한 컨테이너를 복사 없이 참조할 수 있습니다. C++23의 std::mdspan은 이를 다차원으로 확장합니다.
1. std::span 기본
섹션 제목: “1. std::span 기본”#include <span>#include <vector>#include <iostream>
void print_elements(std::span<const int> s) { for (int v : s) std::cout << v << ' '; std::cout << '\n';}
int main() { std::vector<int> v = {1, 2, 3, 4, 5}; int arr[] = {10, 20, 30};
print_elements(v); // vector → span print_elements(arr); // C 배열 → span print_elements({v.data(), 3}); // 처음 3개만}2. 정적 익스텐트 vs 동적 익스텐트
섹션 제목: “2. 정적 익스텐트 vs 동적 익스텐트”#include <span>
// 동적 익스텐트 (크기를 런타임에 결정)std::span<int> dynamic_span;
// 정적 익스텐트 (크기를 컴파일 타임에 고정)std::span<int, 4> static_span; // 항상 4개
int arr[4] = {1, 2, 3, 4};std::span<int, 4> s = arr; // OK// std::span<int, 3> s2 = arr; // 컴파일 오류: 크기 불일치3. 부분 뷰 — subspan, first, last
섹션 제목: “3. 부분 뷰 — subspan, first, last”#include <span>#include <array>
std::array<int, 8> data = {0,1,2,3,4,5,6,7};std::span<int> view = data;
auto front3 = view.first(3); // {0,1,2}auto back3 = view.last(3); // {5,6,7}auto middle = view.subspan(2, 4); // {2,3,4,5}4. 바이트 뷰 — as_bytes / as_writable_bytes
섹션 제목: “4. 바이트 뷰 — as_bytes / as_writable_bytes”#include <span>#include <cstddef>
void serialize(std::span<const std::byte> bytes) { // 바이트 단위 직렬화}
float f = 3.14f;auto bytes = std::as_bytes(std::span{&f, 1});serialize(bytes);5. C++23 std::mdspan — 다차원 배열 뷰
섹션 제목: “5. C++23 std::mdspan — 다차원 배열 뷰”#include <mdspan>#include <vector>
int main() { std::vector<double> data(6);
// 2×3 행렬로 해석 std::mdspan<double, std::extents<std::size_t, 2, 3>> mat(data.data());
mat[0, 0] = 1.0; mat[0, 1] = 2.0; mat[1, 2] = 6.0;}6. 동적 extent mdspan
섹션 제목: “6. 동적 extent mdspan”#include <mdspan>#include <vector>
void matrix_multiply( std::mdspan<const double, std::dextents<std::size_t, 2>> A, std::mdspan<const double, std::dextents<std::size_t, 2>> B, std::mdspan<double, std::dextents<std::size_t, 2>> C){ for (std::size_t i = 0; i < A.extent(0); ++i) for (std::size_t j = 0; j < B.extent(1); ++j) for (std::size_t k = 0; k < A.extent(1); ++k) C[i, j] += A[i, k] * B[k, j];}7. span vs pointer+size 비교
섹션 제목: “7. span vs pointer+size 비교”| 항목 | T* + size_t | std::span<T> |
|---|---|---|
| 타입 안전 | ✗ | ✓ |
| 범위 검사 | 없음 | 디버그 모드 |
| STL 호환 | 어려움 | ✓ (ranges) |
| 오버헤드 | 없음 | 없음 (포인터+크기) |
std::span은 함수 인터페이스에서 배열 종류에 상관없이 연속 메모리를 받을 때 표준 방법입니다. T*와 size_t 쌍을 받는 기존 인터페이스를 std::span으로 교체하면 타입 안전성과 가독성이 동시에 향상됩니다.