yohhoyの日記

技術的メモをしていきたい日記

std::begin/endとconstexpr指定

C++14標準ライブラリから配列版のstd::begin/endメンバ関数がconstexpr指定され、constexpr指定付きのstd::cbegin/cendメンバ関数が追加された。また、std::initializer_list<E>のconstexpr対応に伴い、同オーバーロードstd::begin/endメンバ関数もconstexpr指定される*1

// <iterator>標準ヘッダ
template <class C>
auto begin(C& c) -> decltype(c.begin());

template <class C>
auto begin(const C& c) -> decltype(c.begin());

template<class T, size_t N>
constexpr T* begin(T (&array)[N]);  // C++14以降

template<class C>
constexpr auto cbegin(const C& c) -> decltype(std::begin(c));  // C++14以降

// <initializer_list>標準ヘッダ
template<class E>
constexpr const E* begin(initializer_list<E> il);  // C++14以降

これらにより、C++14以降はレガシーC配列と初期化子リストに限ってconstexpr関数内でもbegin/end, cbegin/cendメンバ関数を使用できる。C++14標準コンテナクラスはconstexpr未対応*2。constexpr対応が必要ならSproutライブラリなどを検討のこと。

  • 2016-03-28追記:C++17(C++1z)ではstd::arrayコンテナのconstexpr対応が行われ、レガシーC配列と同様に扱えるようになる(→id:yohhoy:20160328
// C++14以降
#include <iterator>
#include <initializer_list>

// コンテナ版(実質はレガシーC配列用)
template<class C>
constexpr int sum(const C& c)
{
  int acc = 0;
  for (auto i = std::cbegin(c), e = std::cend(c); i != e; ++i)
    acc += *i;
  // または for (auto e : c) { acc += e; } もOK
  return acc;
}

// 初期化子リスト版
constexpr int sum(std::initializer_list<int> il)
{
  int acc = 0;
  for (auto i = std::cbegin(il), e = std::cend(il); i != e; ++i)
    acc += *i;
  // または for (auto e : il) { acc += e; } もOK
  return acc;
}

constexpr int a[] = { 1, 2, 3 };
static_assert(sum(a) == 6, "");
static_assert(sum({1, 2, 3}) == 6, "");

関連URL

*1:http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2012/n3471.html

*2:std::array コンテナのconstexpr変数初期化は可能だが、結局は非constexprな std::begin オーバーロード関数が選択される(本文中では2番目のオーバーロード関数)。C++14以降はconst版 array::operator[] メンバ関数のみconstexpr指定されるため、インデクスによる要素アクセスならばconstexpr関数内で使用可能。