potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20 生のポインタをstd::spanで扱う

std::spanでは生のポインタもSTLコンテナと同様に扱えます1。生ポインタの操作は通常推奨されませんが、WinAPIは連続したメモリ領域に異なる型を複数配置する場合があり、その場合に使用することになります。例えばFILESYSTEM_STATSTICS_EX構造体の直後にEXFAT_STATISTICS/FAT_STATISTICS/NTFS_STATISTICS_EXが不特定個配置される場合です。

次のコードはstd::vector<std::byte>の一部をuint32_t*uint32_tの生ポインタ)とみなしてstd::spanを作成します。

// C++20以降
#include <numeric> // std::iota
#include <span>    // std::span
#include <vector>  // std::vector

int main()
{
    std::vector<std::byte> buffer(5 + 4 * 3);
    // buffer: {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

    // buffer[5]から先をuint32_tのポインタとして扱う。
    auto p{ reinterpret_cast<uint32_t*>(buffer.data() + 5) };

    // 開始イテレーターと要素数で指定する。
    std::span span1(p, 3);
    std::iota(span1.begin(), span1.end(), 1);
    // buffer: {0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0}

    // 開始イテレーターと終了イテレーターで指定する。
    std::span span2(p, p + 3);
    std::iota(span2.begin(), span2.end(), 11);
    // buffer: {0, 0, 0, 0, 0, 11, 0, 0, 0, 12, 0, 0, 0, 13, 0, 0, 0}
}

  1. std::spanはコンストラクタでイテレーターを受け取ります。生のポインタは基本的な演算でイテレーターのように振る舞い、またSTLが生のポインタに対するイテレーター特性std::iterator_traits等の特殊化を提供することから生のポインタはイテレーターのように扱えます。