potisanのプログラミングメモ

プログラミング素人です。昔の自分を育ててくれたネット情報に少しでも貢献できるよう、情報を貯めていこうと思っています。Windows環境のC++やC#がメインです。

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等の特殊化を提供することから生のポインタはイテレーターのように扱えます。