最終更新日時(UTC):
が更新

履歴 編集

function
<span>

std::span::コンストラクタ(C++20)

constexpr span() noexcept;                                     // (1) C++20

template <class It>
constexpr explicit(extent != dynamic_extent)
  span(It first, size_type count);                             // (2) C++20

template <class It, class End>
constexpr explicit(extent != dynamic_extent)
  span(It first, End last);                                    // (3) C++20

template <size_t N>
constexpr span(
  type_identity_t<element_type> (&arr)[N]) noexcept;           // (4) C++20

template <class T, size_t N>
constexpr span(array<T, N>& arr) noexcept;                     // (5) C++20

template <class T, size_t N>
constexpr span(const array<T, N>& arr) noexcept;               // (6) C++20

template <class R>
constexpr explicit(extent != dynamic_extent)
  span(R&& r);                                                 // (7) C++20

constexpr span(const span& other) noexcept = default;          // (8) C++20

template <class OtherElementType, size_t OtherExtent>
constexpr explicit(extent != dynamic_extent && OtherExtent == dynamic_extent)
  span(const span<OtherElementType, OtherExtent>& s) noexcept; // (9) C++20

constexpr explicit(extent != dynamic_extent)
  span(std::initializer_list<value_type> il);                  // (10) C++26

概要

spanオブジェクトを構築する。

  • (1) : デフォルトコンストラクタ。空のspanオブジェクトを構築する
  • (2) : 参照範囲として先頭要素を指すイテレータと、そこからの要素数を指定して、それらの要素を参照するspanオブジェクトを構築する
  • (3) : 参照範囲として先頭要素を指すイテレータと、末尾要素の次を指すイテレータを指定して、それらの要素を参照するspanオブジェクトを構築する
  • (4) : 指定された組み込み配列の全体を参照するspanオブジェクトを構築する
  • (5) : 指定された非const左辺値参照のstd::arrayの全体を参照するspanオブジェクトを構築する
  • (6) : 指定されたconst左辺値参照のstd::arrayの全体を参照するspanオブジェクトを構築する
  • (7) : 指定された、メモリ連続性をもつイテレータを持つオブジェクトの要素全体を参照するspanオブジェクトを構築する
  • (8) : コピーコンストラクタ。otherと同じ範囲を参照するspanオブジェクトを構築する
  • (9) : テンプレートパラメータの異なるspanオブジェクトを変換する。以下のような変換ができる:
    • 静的な要素数をもつspanから動的な要素数をもつspanへの変換。
    • 動的な要素数をもつspan同士の変換
    • span<T>からspan<const T>への変換
    • バイト数が同じ暗黙の型変換が可能な要素型をもつspan同士の変換
  • (10) : 初期化子リストからspanオブジェクトを構築する

テンプレートパラメータ制約

  • (1) :
    • Extent == dynamic_extent || Extent == 0trueであること
      • -1はオーバーフローによって正の最大値になるのでfalse
  • (2) :
  • (3) :
  • (4), (5), (6) :
    • extent == dynamic_extent || N == extenttrueであること
    • remove_pointer_t<decltype(data(arr)))>を型Uであるとして、
      • is_convertible_v<U(*)[], element_type(*)[]>trueであること (この制約の意図は、配列の要素型からelement_typeへ、修飾の変換のみを許可すること)
  • (7) :
    • Ustd::remove_reference_t<std::iter_reference_t<R>>とするとき
      • R はコンセプト std::ranges::contiguous_range 及び std::ranges::sized_range を満たしていること
      • R がコンセプト std::ranges::borrowed_range を満たすか、std::is_const_v<element_type>trueであること
      • std::remove_cvref_t<R>std::spanの特殊化ではないこと
      • std::remove_cvref_t<R>std::arrayの特殊化ではないこと
      • std::is_array_v<std::remove_cvref_t<R>>false であること
      • std::is_convertible_v<U(*)[], element_type(*)[]>true であること。(この制約の意図は、イテレータ参照型から element_type への修飾変換のみを許可すること)
  • (9) :
    • extent == dynamic_extent || OtherExtent == dynamic_extent || extent == OtherExtenttrueであること (受け取り側がdynamic_extentを持っていれば任意のExtentから変換できる)
    • OtherElementType(*)[]型がElementType(*)[]型に変換可能であること
  • (10) :
    • is_const_v<element_type>trueであること
      • (std::span<const T>に対してのみ使用できる)

事前条件

効果

  • (2) : イテレータ範囲[first, first + count)を参照するspanオブジェクトを構築する
  • (3) : イテレータ範囲[first, last)を参照するspanオブジェクトを構築する
  • (4), (5), (6) : 範囲[data(arr), data(arr) + N)を参照するspanオブジェクトを構築する
  • (7) : 範囲[std::ranges::data(r), std::ranges::data(r) + std::ranges::size(r))を参照するspanオブジェクトを構築する
  • (9) : 範囲[s.data(), s.data() + s.size())を参照するspanオブジェクトを構築する
  • (10) : 範囲[il.begin(), il.begin() + il.size())を参照するspanオブジェクトを構築する

事後条件

例外

  • (1), (2), (4), (5), (6) : 投げない
  • (3) : last - firstがなんらかの例外を送出する可能性がある
  • (7) : コンテナ型によっては、std::ranges::data(r)std::ranges::size(r)の呼び出しがなんらかの例外を送出する可能性がある

計算量

  • (1)-(10) : 定数時間

この機能が必要になった背景・経緯

備考

  • (10) : std::span<const int> v = {1, 2, 3}; のような使い方は初期化子リストの寿命が尽きてしまうので注意。関数のパラメータをspanとして受け取るような使い方が想定される

基本的な使い方 (C++20)

#include <cassert>
#include <span>
#include <vector>
#include <array>
#include <string>

int main()
{
  std::vector<int> v = {1, 2, 3, 4, 5};

  // (1) デフォルトコンストラクタ
  {
    // 長さ0の参照範囲をもつspanオブジェクト
    std::span<int, 0> s1;
    assert(s1.empty());

    // 動的な要素数をもつspanオブジェクト
    std::span<int> s2;
    assert(s2.empty());

    // 以下はコンパイルエラーになる。
    // 長さ1以上のspanは、参照範囲を設定しなければならない
    // std::span<int, 1> s3{};
  }

  // (2) イテレータと要素数の組を指定
  {
    // vの先頭3要素を参照する。
    std::span<int> s{v.begin(), 3};
    assert(s.size() == 3);
    assert(s[0] == 1);
    assert(s[1] == 2);
    assert(s[2] == 3);
  }

  // (3) 範囲を指定
  {
    std::span<int> s{v.begin(), v.begin() + 3};
    assert(s.size() == 3);
    assert(s[0] == 1);
    assert(s[1] == 2);
    assert(s[2] == 3);
  }

  // (4) 組み込み配列への参照を指定
  {
    int ar[] = {1, 2, 3, 4, 5};
    std::span<int> s{ar};
    assert(s.size() == 5);
    assert(s.data() == ar); // 元の配列をコピーせず、参照している
  }

  // (5) std::arrayオブジェクトへの参照を指定
  {
    std::array ar = {1, 2, 3, 4, 5};
    std::span<int> s{ar};
    assert(s.size() == ar.size());
    assert(s.data() == ar.data());
  }

  // (6) const std::arrayオブジェクトへの参照を指定
  {
    std::array ar = {1, 2, 3, 4, 5};
    const auto& car = ar;
    std::span<const int> s{car};

    assert(s.size() == car.size());
    assert(s.data() == car.data());
  }

  // (7) メモリの連続性をもつイテレータをもつオブジェクトの要素全体を参照させる
  {
    std::span<int> s1{v};
    assert(s1.size() == v.size());
    assert(s1.data() == v.data());

    // std::string_viewの代わり
    std::string str = "Hello";
    std::span<char> s2{str};
    assert(s2.size() == str.size());
    assert(s2.data() == str.data());
  }

  // (8) コピーコンストラクタ
  {
    std::span<int> s1{v};
    std::span<int> s2 = s1;

    // コピー元とコピー先が同じ範囲を参照する
    assert(s1.data() == v.data());
    assert(s2.data() == v.data());
  }

  // (9) 変換コンストラクタ
  {
    int ar[] = {1, 2, 3};

    std::span<int, 3> s1{ar};
    std::span<int> s2 = s1;
    std::span<int> s3 = s2.first(2);
    std::span<const int> s4 = s3;

    assert(s4.size() == 2);
    assert(s4.data() == ar);
  }
}

出力

初期化子リストからspanを構築する (C++26)

#include <iostream>
#include <span>
#include <vector>

void print_list(std::span<const int> s) {
  bool first = true;
  for (const int& x : s) {
    if (first) {
      first = false;
    }
    else {
      std::cout << ',';
    }
    std::cout << x;
  }
  std::cout << std::endl;
}

int main()
{
  std::vector<int> v = {1, 2, 3, 4, 5};
  print_list(v);

  print_list({{3, 1, 4}}); // C++20からOK
  print_list({3, 1, 4});   // C++26からOK
}

出力

1,2,3,4,5
3,1,4
3,1,4

バージョン

言語

  • C++20

処理系

  • Clang: (10.0.0 現在、実装は P1394R4 以前の不完全なものである)
  • GCC: 10.0.1
  • Visual C++: ??

参照