Boost 1.62.0がリリースされました

Boost 1.62.0がリリースされました。

リリースノートはいつものように、boostjpサイトで翻訳したものを公開しています。Flastさん、協力ありがとうございました。

新ライブラリ

今回の新ライブラリは、FiberとQVMの2つです。

Boost.Fiberは、ユーザースレッドのライブラリです。コルーチンをスレッドのインタフェースで使えるようにしたようなものです。軽量スレッドとして使えます。

Boost.QVMは、クォータニオン、ベクトル、行列のライブラリです。コンセプトベースで設計されているので、自分で作った型に対して、Boost.QVMのアルゴリズムを適用できます。

主要な更新

  • Boost.Containerに、C++1z関係の機能が入った。連想コンテナのsplice機能や、try_emplace()など
  • Boost.Coroutineライブラリが非推奨化した。Boost.Coroutine2への移行を推奨
  • Boost.Logに、プロセス間ロギングの機能が入った

今回はバグ修正が多く、目立った新機能はそれほどありません。

C++1z std + 数字の名前空間を予約

C++の今後のメジャーバージョンアップで標準ライブラリに大きな変更を加えるときのために、「std + 数字」の名前空間が予約されます。std2とかの名前空間を作りたいのだそうです。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z 並列アルゴリズムライブラリ

C++1zでは、並列アルゴリズムのライブラリが導入されることになりました。このライブラリは、<algorithm>, <numeric>, <memory>で定義されるアルゴリズムオーバーロードという形で提供されます。

using namespace std::execution; // 実行ポリシーの名前空間

std::vector<int> v = …

std::sort(v.begin(), v.end());      // これまで通りの順序実行
std::sort(seq, v.begin(), v.end()); // 明示的に順序実行を指定

std::sort(par, v.begin(), v.end());       // 並列実行を許可
std::sort(par_unseq, v.begin(), v.end()); // 並列and/orベクトル実行を許可

このライブラリの設計は、Thrustが元になっています。

実行ポリシー

並列アルゴリズムには、実行ポリシー (execution policy) を指定して使用します。実行ポリシーには、以下の種類があります:

実行ポリシー ポリシー値
順序実行
(これまで通りのシングルスレッド実行)
sequential_execution_policy seq
並列実行 parallel_policy par
並列実行および/もしくはベクトル実行 parallel_unsequenced_policy par_unseq

注意としては、実行ポリシーとして並列実行を指定した場合、規格上は「並列実行を許可する」というリクエストの意味になるということです。コンパイラやその他環境条件によっては、シングルスレッドで実行されるかもしれません。

実行ポリシーの定義は、新設される<execution>ヘッダで行われます。

実行ポリシーごとに型が付けられ、それらの型でオーバーロードができます。並列アルゴリズムの宣言としては、以下のようになります:

template <class ExecutionPolicy,
          class InputIterator, class Function>
void for_each(ExecutionPolicy&& exec,
              InputIterator first, InputIterator last,
              Function f);

並列アルゴリズムに対応するアルゴリズム

並列アルゴリズムは、<algorithm>, <numeric>, <memory>の全てが対象にはなりません。2016年9月段階では、以下のアルゴリズムが対象となります。

  • <algorithm>

    • adjacent_find
    • all_of
    • any_of
    • copy
    • copy_if
    • copy_n
    • count
    • count_if
    • equal
    • fill
    • fill_n
    • find
    • find_end
    • find_first_of
    • find_if
    • find_if_not
    • for_each
    • for_each_n (new)
    • generate
    • generate_n
    • includes
    • inplace_merge
    • is_heap
    • is_heap_until
    • is_partitioned
    • is_sorted
    • is_sorted_until
    • lexicographical_compare
    • max_element
    • merge
    • min_element
    • minmax_element
    • mismatch
    • move
    • none_of
    • nth_element
    • partial_sort
    • partial_sort_copy
    • partition
    • partition_copy
    • remove
    • remove_copy
    • remove_copy_if
    • remove_if
    • replace
    • replace_copy
    • replace_copy_if
    • replace_if
    • reverse
    • reverse_copy
    • rotate
    • rotate_copy
    • search
    • search_n
    • set_difference
    • set_intersection
    • set_symmetric_difference
    • set_union
    • sort
    • stable_partition
    • stable_sort
    • swap_ranges
    • transform
    • unique
    • unique_copy
  • <numeric>

    • adjacent_difference
    • exclusive_scan (new)
    • inclusive_scan (new)
    • inner_product
    • reduce (new)
    • transform_exclusive_scan (new)
    • transform_inclusive_scan (new)
    • transform_reduce (new)
  • <memory>

    • uninitialized_copy
    • uninitialized_copy_n
    • uninitialized_fill
    • uninitialized_fill_n

<numeric>については、並列アルゴリズムのほとんどが新たに作られることになります。

exclusive_scan()inclusive_scan()は、partial_sum()の亜種です。inclusive_scan()partial_sum()と同様に、先頭要素が出力に含まれます。exclusive_scan()の出力に先頭要素は含まれません。

reduce()accumulate()の亜種で、実行順序が非決定的になります。

transform_exclusive_scan()transform_inclusive_scan()は、1引数をとる変換関数で要素を変換した結果を、2引数をとる集計関数に渡します。transform + scanを同時に行います。transform_reduce()も同様に、transform + reduceを同時に行います。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z 初期化子リストからpairとtupleを構築しやすくするための改善

C++14では、以下のコードがコンパイルエラーになります。

std::tuple<int, int> pixel_coordinates() 
{
    return {10, -15};  // コンパイルエラー
}

struct NonCopyable { NonCopyable(int); NonCopyable(const NonCopyable&) = delete; };

std::pair<NonCopyable, double> pmd{42, 3.14};  // コンパイルエラー

C++1zではこれらが通るように、pairtupleのコンストラクタが、explicitと非explicitで適切にオーバーロードされるようになります。

explicitと非explicitでのオーバーロードにはSFINAEを使用しますが、規格書上のコンストラクタの定義にはenable_ifのような具体的な実装については出てこないため、EXPLICITと大文字で宣言が記述され、その定義の文章で「この場合はexplicit (、そうでなければ非explicit) となる」のような記述がされます。

例:

EXPLICIT constexpr pair(const T1& x, const T2& y);

注: …(略)… このコンストラクタは、is_convertible<const first_type&, first_type>::valuefalseもしくはis_convertible<const second_type&, second_type>::valuefalseである場合にのみexplicitとなる。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z has_unique_object_representations型特性

少しまえから、オブジェクトからハッシュ値の計算を自動的に行いたい、という話がでていました。C++1z時点ではそのサポートは入りませんが、前準備として、自動的にハッシュ値を求められる型かを判別するためのhas_unique_object_representationsという型特性が入ります。

この型特性は、パディングビットを持たないtrivially copyableな型(memcpyできる型)や、その配列に対して真を返します。

宣言

// <type_traits>
namespace std {
    template <class T>
    struct has_unique_object_representations : bool_constant<…> {};
}

2016年9月段階では、変数テンプレート版は予定されていません。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

ElixirでExcelデータを読み込むライブラリ

ElixirでExcelデータを読み込むライブラリはいくつかありますが、どれも自分の用途には微妙に適合しませんでした。インタフェースと戻り値のフォーマットはexcellentライブラリが好きだったのですが、Excel 2000のデータしか読み込めなかったので、xlsx_parserライブラリをラップしてexcellentライブラリライクに操作できるライブラリを書きました。

読み方は「エクセリオン」です。昔に同名のゲームがあったようですが、LとRが違います。s/excerion/excelion/ 。元ネタはゲームではなく小説の『黒の魔王』です。

xlsx_parserライブラリに、シート名のリストを取得する機能がなかったので、ライブラリの実装を調べてその機能を拡張しました。その機能はxlsx_parserライブラリにissueとして送ったらv0.0.8で取り込んでもらえました。

あとは、excellentライブラリと違う点として、読み込みを開始する行番号を指定できるようにしてあります。

ドキュメントはとりあえずソースコードに書いてありますが、余裕ができてきたらex_docで書きます。

C++1z 未初期化メモリのアルゴリズムと、デストラクタ呼び出しの関数

C++標準ライブラリには、データ構造を実装する際に使用する未初期化メモリに対するアルゴリズム<memory>ヘッダで定義されています。

C++1zでは、厳しいパフォーマンス要求があるデータ構造を実装しやすくするためのアルゴリズムが、いくつか定義されます。これらは、std::vectorの内部実装や、EASTL、follyといったライブラリの実装で使われていたものです。

関数 説明
std::destroy_at()
std::destroy()
std::destroy_n()
指定された単一要素もしくは範囲の要素に対してデストラクタを呼び出す
(配置newした要素に対してよく使う)
std::uninitialized_move()
std::uninitialized_move_n()
未初期化メモリから要素をムーブ構築する
std::uninitialized_value_construct()
std::uninitialized_value_construct_n()
未初期化メモリから要素を値初期化で構築する
std::uninitialized_default_construct()
std::uninitialized_default_construct()
未初期化メモリから要素をデフォルト構築する

宣言

// <memory>
namespace std {
  template <class T>
  void destroy_at(T* location);

  template <class ExecutionPolicy, class ForwardIterator>
  void destroy(ExecutionPolicy&& exec, //see [algorithms.parallel.overloads]
               ForwardIterator first, ForwardIterator last);

  template <class ForwardIterator>
  void destroy(ForwardIterator first, ForwardIterator last);

  template <class ExecutionPolicy, class ForwardIterator, class Size>
  ForwardIterator destroy_n(ExecutionPolicy&& exec,//see [algorithms.parallel.overloads]
                            ForwardIterator first, Size n);

  template <class ForwardIterator, class Size>
  ForwardIterator destroy_n(ForwardIterator first, Size n);

  template <class ExecutionPolicy, class InputIterator, class ForwardIterator>
  ForwardIterator uninitialized_move(ExecutionPolicy&& exec,//see [algorithms.parallel.overloads]
                                     InputIterator first, InputIterator last, ForwardIterator result);

  template <class InputIterator, class ForwardIterator>
  ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result);

  template <class ExecutionPolicy, class InputIterator, class Size, class ForwardIterator>
  pair<InputIterator,ForwardIterator> uninitialized_move_n(ExecutionPolicy&& exec, //see [algorithms.parallel.overloads]
                                      InputIterator first, Size n, ForwardIterator result);

  template <class InputIterator, class Size, class ForwardIterator>
  pair<InputIterator,ForwardIterator> uninitialized_move_n(InputIterator first, Size n, ForwardIterator result);

  template <class ExecutionPolicy, class ForwardIterator>
  void uninitialized_value_construct(ExecutionPolicy&& exec, //see [algorithms.parallel.overloads]
                                     ForwardIterator first, ForwardIterator last);
  template <class ForwardIterator>
  void uninitialized_value_construct(ForwardIterator first, ForwardIterator last);

  template <class ExecutionPolicy, class ForwardIterator, class Size>
  ForwardIterator uninitialized_value_construct_n(ExecutionPolicy&& exec, //see [algorithms.parallel.overloads]
                                                  ForwardIterator first, Size n);

  template <class ForwardIterator, class Size>
  ForwardIterator uninitialized_value_construct_n(ForwardIterator first, Size n);

  template <class ExecutionPolicy, class ForwardIterator>
  void uninitialized_default_construct(ExecutionPolicy&& exec, //see [algorithms.parallel.overloads]
                                       ForwardIterator first, ForwardIterator last);

  template <class ForwardIterator>
  void uninitialized_default_construct(ForwardIterator first, ForwardIterator last);

  template <class ExecutionPolicy, class ForwardIterator, class Size>
  ForwardIterator uninitialized_default_construct_n(ExecutionPolicy&& exec, //see [algorithms.parallel.overloads]
                                                   ForwardIterator first, Size n);

  template <class ForwardIterator, class Size>
  ForwardIterator uninitialized_default_construct_n(ForwardIterator first, Size n);
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z unique_ptrのテンプレート代入演算子に、不足していたSFINAEルールを追加

unique_ptrの以下の代入演算子に、オーバーロード解決に参加する条件が不足していました。

template <class U, class E>
unique_ptr& operator=(unique_ptr<U, E>&& u) noexcept;

C++14時点では、以下の2つの条件になっています。

  • unique_ptr<U, E>::pointerが、pointerに暗黙変換可能な型であること。
  • Uが配列型ではないこと。

C++1zでは、これに以下の条件が追加されます。

  • is_assignable_v<D&, E&&>trueであること

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z 乱数用語を変更

C++11で導入された乱数ライブラリでは、乱数生成器のコンセプトに 「URNG (Uniform Random Number Generator, 一様乱数生成器)」という用語を使用していました。

しかし、一般的なURNGの用語とは異なり、C++の乱数生成器は一度の呼び出しで、(32ビットを超えるような) より多くのビットを単一の符号なし整数にパックして返すという動作が許可されています。URNGという用語を使用することは動作を誤解させることにつながりそう、と判断され、C++1zでは「URBG (Uniform Random Bit Generator)」という用語が代わりに使われるようになります。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z memory_order_consumeを一時的に非推奨化

並行プログラミングでのアトミック操作をする上で、処理の順序保証をする仕組みとしてメモリオーダーがあります。C++の標準ライブラリではメモリオーダーが何種類か提供されていますが、C++1zではmemory_order_consumeというメモリオーダーが一時的に非推奨になります。

memory_order_consumeは、データ依存性 (Data dependency) を調べて関連するデータの処理順序を保証するというものですが、ユーザーコミュニティから「その定義が現実に即していない」「acquire/releaseより弱いから使いにくい」といった意見が多く上がったため、よりよい定義に変更するまでの間、一時的にmemory_order_consumeが非推奨となります。

仕様上は「非推奨 (deprecated)」という扱いではなく、「仕様検討中」という注釈が付きます。

関連する機能として、std::kill_depedency()関数や、[[carries_dependency]]属性があります。これらの機能もしばらく使わないことになるでしょう。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。