potisanのプログラミングメモ

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

C++20 MSVS 2019ではstd::span<T>のイテレーター初期化は不正な要素を参照しないように丸められる

std::spanMicrosoft Visual Studio Community 2019 Preview Version 16.11.0 Preview 3.0の現時点の最新環境における動作確認結果です。

std::span<T>イテレーター(beginend)で初期化するとき、参照範囲は不正な要素を参照しないように丸められます。例えばstd::span<std::uint8_t>イテレーターからstd::span<std::uint32_t>を作成するとき、std::span<std::uint8_t>の余剰部分はstd::span<std::uint32_t>の参照対象外となります。

  • std::span<std::uint8_t>{0x00, 0x01, 0x02, 0x03, 0x04, 0x05}
  • std::span<std::uint32_t>{0x03020100} // 0x04、0x05は参照されない。

cpprefjp(リンク)を確認する限りstd::spanイテレーター初期化は「妥当な範囲」を受け取るので、型のバイト数が異なる場合は余剰部分の無視により妥当にしていると推察されます(規格書未確認)。

以下は動作確認用のコードです。std::uint8_tからstd::uint32_tに変換して再度std::uint8_tに戻すことで参照する範囲の変化を確かめています。

#include <span>
#include <iostream>

int main()
{
    // バイト配列のスパンを作成
    auto span1 = std::span(std::initializer_list<std::uint8_t>{ 0, 1, 2, 3, 4, 5 });
    // 32ビット整数配列のスパンに変換
    auto span2 = std::span(
        reinterpret_cast<const std::uint32_t*>(span1.data()),
        reinterpret_cast<const std::uint32_t*>(span1.data() + span1.size()));
    // バイト配列のスパンに変換
    auto span3 = std::span(
        reinterpret_cast<const std::uint8_t*>(span2.data()),
        reinterpret_cast<const std::uint8_t*>(span2.data() + span2.size()));

    // uint8_tのまま出力すると文字(char)扱いなので整数に変換して出力する。
    for (auto value : span1) std::cout << static_cast<int>(value) << ",";
    std::cout << std::endl;
    for (auto value : span3) std::cout << static_cast<int>(value) << ",";
    std::cout << std::endl;
    // 出力
    // 0, 1, 2, 3, 4, 5,
    // 0, 1, 2, 3,
}