🧤

配列の利便性を高めるstd::span

2025/03/08に公開

以下のような配列が渡せる関数はよく見かけます。

#include <iostream>

void print_data(const float* data, std::size_t data_size) {
    std::cout << "Number of entries: " << data_size << std::endl;
    for (int i = 0; i < data_size; ++i) {
        std::cout << data[i] << std::endl;
    }
}

int main() {
    const std::size_t data_size(9);
    const float data[data_size]{0, 1, 2, 3, 4, 5, 6, 7, 8};
    print_data(data, data_size);
    return 0;
}

生配列だけ渡すと、ただのポインタに成り下がってしまい、要素数が分からなくなってしまうため、要素数とセットで渡さないといけないです。
std::arrayとかstd::vectorを使えば、size()という関数でいつでも要素数を取得できますが、何等かの理由で生配列を使わないといけない場合は、やはり上記のような関数になってしまいます。

でも C++20 以降は、std::spanというクラスが使えるようになりました。

#include <iostream>
#include <span>

void print_data(std::span<const float> data) {
    std::cout << "Number of entries: " << data.size() << std::endl;
    for (const auto& number : data) {
        std::cout << number << std::endl;
    }
}

int main() {
    const std::size_t data_size(9);
    const float data[data_size]{0, 1, 2, 3, 4, 5, 6, 7, 8};
    print_data(data);
    return 0;
}

std::spanは「生配列、std::arraystd::vector」から作成できますが、std::arraystd::vectorと違って、所有権を持たない既存のメモリのビューになっているため、追加でメモリをアロケートしませんし、コピーも発生しないです。
なのに、std::arraystd::vectorのように、size()や、empty()front()back()data()などのメンバー関数を持っていて、非常に使いやすいです。

例えば、以下のようにstd::vectorから作成してから一部を渡せます。

int main() {
    std::vector<float> data{0, 1, 2, 3, 4, 5, 6, 7, 8};
    print_data(std::span{data});                    // 全部渡す
    print_data(std::span{data}.first(3));           // 最初の3個の要素のみ渡す
    print_data(std::span{data}.subspan(3, 3));      // 真ん中の3個の要素のみ渡す
    print_data(std::span{data}.last(3));            // 最後の3個の要素のみ渡す
    return 0;
}

C++20で追加された数ある機能の中で一番シンプルですぐ使える機能だと思うので、C++20が使える環境になったら是非使ってみてください。


|cpp記事一覧へのリンク|

Discussion