2015-02-17

C++標準: 2015-02 mailingsのレビュー: N4340-N4359

2015-02 mailingsが公開された。

N4340: Remove Deprecated Use of the register Keyword

registerキーワードを非推奨扱いに変更する提案。将来的には廃止して、registerキーワードは別の機能に使うことができるようになるだろう。

N4346: Multidimensional bounds, index and array_view, revision 5

連続したストレージを多次元配列に見せかけるライブラリ、array_viewの提案。

N4349: WG21 2014-12-05 Ballot Resolution Telecon Minutes

2014年12月5日に行われた電話会議の議事録。

[面倒なPDF] N4350: Agenda and Meeting Notice for WG21 Concepts Meeting Notice (revision 1)

2015年1月15日に行われたConcept会議の予定表。

[PDF注意] N4351: Responses to National Body Comments, PDTS 19570, C++ Extensions for Parallelism

並列実行アルゴリズムライブラリに対するNBコメントに対する返答。今回、C++WG JPからは結構なコメントが出ている。

JP1のtypo報告は受理。

JP2の純粋なベクトルポリシーを付け加える提案は、議論する時間が足りないために却下。

JP3、規格はベクトル化非安全となる条件を定義しているが、具体的にどの標準ライブラリの関数が該当するのかわかりにくい。該当する標準ライブラリ関数の一覧を作成すべきであるという意見は、却下されている。

JP4、実行ポリシーを受け取るオーバーロードがないとする意見は却下。既存の文面は該当のオーバーロードの存在を暗に規定しているとのこと。

N4352: Technical Specification for C++ Extensions for Parallelism, Working Draft

並列実行アルゴリズムライブラリ、Parallelism TSのドラフト。

#include <experimental/algorithm>

int main()
{
    int a[10] = { 1,2,3,4,5,6,7,8,9,10 } ;

    // 並列実行される
    std::for_each( par, std::begin(a), std::end(a),
        [&]( auto & x ) { x *= 2 ; } ) ;

    // シリアル実行
    bool b1 = std::is_sorted( std::begin(a), std::end(a) ) ;
    bool b2 = std::is_sorted( seq, std::begin(a), std::end(a) ) ;

    // パラレル実行
    bool b3 = std::is_sorted( par, std::begin(a), std::end(a) ) ;

    // パラレルベクトル実行
    
    bool b4 = std::is_sorted( par_vec, std::begin(a), std::end(a) ) ;
}

並列実行アルゴリズムは、既存のアルゴリズムに対するオーバーロードで提供される。seqはシリアル実行、parがパラレル実行(スレッドなど)、par_vecがパラレルベクトル実行(スレッド+SIMDやGPGPUなど)

[PDFである必要がない] N4353: Parallelism TS - Editor's Report

Parallelism TSの編集者の報告書。変更点がまとめられている。

[PDF] N4354: Parallelism TS - DTS Ballot Document

おなじくParallelismのドラフト

[PDF] N4355: Shared Multidimensional Arrays with Polymorphic Layout

array_viewとshared_ptrを組み合わせたshared_view/weak_viewの提案。

多次元配列のレイアウトは古い問題だ。C/C++で伝統的なフォーマット、FORTRANで伝統的なフォーマットなどなど。多次元配列の処理がハードウェアで実装されている場合に、そのハードウェアがサポートするレイアウトにあわせる必要があるなど。

レイアウトを変更する場合、ソースコードの大規模なリファクタリングや、あるいは本質的には同一のコードが重複するなどの煩わしいことが起こる。

これを防ぐために、ポリモーフィックにレイアウトを指定できるarary_viewを提案している。

[PDf] N4356: Relaxed Array Type Declarator

配列の宣言子の制限緩和の提案。

多次元配列の要素数は、最初のものだけが省略可能である。

T [ N0 ] [ N1 ] [ N2 ] ...

この場合、N0のみが省略可能となる。

この提案は、N4355で提案されている、shared_viewのために、最初以外の要素数の省略を認める。つまり、N1やN2も省略可能になる。これはどのように使うかというと、以下のように、テンプレート実引数のための型として使う。

std::shared_array< T[N1opt][N2opt][N3opt]...,  Layoutopt >

省略した部分は、実行時に指定できる設計にできる。

N4357: [[noexit]] attribute for main

main関数のみに指定できるattributeである[[noexit]]の提案。これを指定すると、main関数からは戻らないことを指定したことになり、staticストレージ上のオブジェクトのデストラクター呼び出しを抑制できる。すなわち、その分のコード生成をする必要がない。

前回の提案、N4226では、既存の[[noreturn]]をmain関数に指定した時にそのような意味にしようと提案していたが、今回は、別の名前が提案されている。

N4226をUrbana-Champaign会議で議論したところ、実装するにはあまり好ましくないリンカーマジックが必要になるので、その実装方法についてより議論が必要だとしている。

N4358: Unary Folds and Empty Parameter Packs

folding expressionでoperator +, operator *, operator &, operator |で空のパラメーターパックを展開した時のフォールバック値を廃止する。

N4295で提案されているfolding expressionは、パラメーターパックに対して以下のように演算子を適用できた。

template < typename ... Types >
auto sum( Types && ... args )
{
    // args#0 + args#1 + ... + args#N
    return (args + ...) ;
}
 
int main()
{
    // 6
    sum( 1, 2, 3 ) ;
}

N4295で提案されたFolding Expressionは、空のパラメーターパックを展開した場合、一部の演算子にフォールバック値が定義されていた。たとえば、operator +の場合は0、operator *の場合は1

sum() ; // 0

N4358は、一部の演算子について、このフォールバック値を問題あるものとして、廃止する。

当初の設計思想として、フォールバック値は、その演算でよく使われる単位元ではある。operator +の場合は0、operator *の場合は1となっている。しかし、これは純粋に数学的な処理ぐらいにしか役に立たない。

例えば、ベクトル型のクラスVectorTypeがあるとして、そのoperator =はスカラー値を取って、すべての要素をその値にするような処理をするかもしれない。

VectorType vec = { 1, 2, 3, 4, 5 } ;

vec = ( some_vecs + ... ) ;

もし、パラメーターパックsome_vecsが空であった場合、vec = 0 となってしまう。これはVectorTypeクラスの設計次第で、意図しない結果をもたらすかもしれない。

そのため、N4358は廃止を提案している。operator &&, operator ++, operator ,に対しては、これらをオーバーロードするのは通常は推奨できないし、それでもオーバーロードする一部のDSLライブラリなどは、十分に考慮されているので、問題にはならないとして維持するという。

もし、フォールバック値が欲しい場合、binary foldingを使えばよい。

VectorType vec = { 1, 2, 3, 4, 5 } ;

vec = ( some_vecs + ... + 0 ) ;

N4359: A Proposal to Add vector release method just like unique_ptr release method to the Standard Library

vector::releaseの提案

これはunique_ptrのreleaseと同じ発想だ。vectorの内部のストレージの先頭へのポインターが返される。そして、vectorはそのストレージの所有権を放棄する。

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

    auto size = v.size() ;
    auto alloc = v.get_allocator() ;
    int * ptr = v.release() ;

    alloc.dealloc( ptr, size ) ;
}

vectorは所有権を放棄するので、ストレージの破棄や、非トリビアルな型の場合、デストラクター呼び出しも自分でやらなければならない。vectorの所有するストレージを奪うことにより、従来ならばコピーせざるをえなかった低級な処理でも、そのままアドレスを渡すことができる。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

3 comments:

Anonymous said...

「この場合、N0のみが省略可能となる。」
からの部分が等幅フォントになっているので修正お願いします。

Anonymous said...

vectorの所有権の話ですが、所有権を手放す場合ってあんまりいい状況じゃないと思います。
vectorにはoperator&が定義されていますし、それ以上のことが必要でしょうか。
低級処理に所有権を渡すのであれば最初から生ポで処理するべきじゃないでしょうか。
デストラクタも管理されますし。
処理系依存な気がする、vectorの内部形式を把握しないといけません。
なんか時代と逆行してないですか?

Anonymous said...

> vector::release
確保すべき具体的なサイズが不明のストレージをvectorの自動拡張を利用して動的にセットアップ->所有権ごとストレージへのポインタを要求する関数に渡す、みたいな用途でしょうな。

確保-解放の整合性は関数側での解放仕様にあわせた適切なアロケータの指定で対応すると。