本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

C++標準化委員会の文書のレビュー: P0021R0-P0029R0

P0021R0: Working Draft, C++ Extensions for Ranges

Range TSのドラフト。コンセプトを使ったRangeライブラリの提案。

イテレーターのペアを扱うよりも、Rangeという単位で簡単に扱うことができる。

ユーザーにわかりやすい恩恵としては、algorithmにイテレーターのペアではなくコンテナーを渡せるようになる。単に使う際にはコンセプトを意識する必要はない。

P0022R0: Proxy Iterators for the Ranges Extensions

vector<bool>のようなプロクシーイテレーターをRangeに対応させるためにRange提案を修正する提案。既存のiter_swapに加えて、iter_moveを追加して、プロクシーイテレーターのためのカスタマイゼーションポイントとする。

コンセプトにマップがあればもっと楽になるのに。

P0024R0: The Parallelism TS Should be Standardized

既存のalgorithmを並列実行させ、また並列計算のためにnumericライブラリを追加するParallelism TSを、標準規格に取り入れるべきだという提案。

Parallelism TSの実装経験は豊富であり、C++17に加えるに足る。

Parallelism TSは、たとえば、以下のようにvector<int>の各要素に処理を加えるコードがある。

void process_data( std::vector<int> const & data )
{
    std::for_each( std::begin(data), std::end(data),
        []( auto e ) { DoSomething(e) ; } 
    ) ;
}

このコードは、以下のように変更を加えるだけで、並列実行させることができる。


std::for_each( std::par, std::begin(data), std::end(data), ...

このように、既存のalgorithmに並列実行版のオーバーロードを加える。また、並列計算用のnumericが用意されている。

すでに、Microsoft, HPX, Codeplay, HSA, Thibaut Lutz, NVIDIAが独立してParallelism TSを公に実装している。

P0025R0: clamp: An algorithm to 'clamp' a value between a pair of boundary values (Draft) -

標準ライブラリにclamp( value, min_value, max_value )を追加する提案。valueがmin_valueより小さいならmin_valueを、max_valueより大きいならmax_valueを、そうでなければvalueを返す。

比較関数をとるオーバーロード、clamp( value, min, max, comp)も追加される。

P0026R0: multi-range-based for loops

Range-based forの拡張提案。

range-based forはコンテナーの各要素に対する処理を簡単に書くことができる。

std::vector<int> v ;

for ( auto && elem : v )
{
    DoSomething( elem ) ;
}

しかし、現実では、複数のコンテナーのそれぞれの各要素を先頭から同時にアクセスしたいことがよくある。そういう場合、古典的なforループを使わざるを得ない。

std::vector<int> v1, v2 ;

auto iter1 = v1.begin() ;
auto iter1_end = v1.end() ;
auto iter2 = v2.begin() ;
auto iter2_end = v2.end() ;

for( ; iter1 != iter1_end && iter2 != iter2_end ;
    ++iter1, ++iter2 )
{
    DoSomething( *iter1, *iter2 ) ;
}

そこで、range-based forを拡張して、セミコロンで区切ることで、複数のRangeを受け取れるようにする。

// P0026提案

for ( auto && e1 : v1 ; auto && e2 : v2 )
{
    DoSomething( e1, e2 ) ; 
}

レンジの要素の数が同じではない場合、要素数が少ないレンジの終端に達した時点でループが打ち切られる。

P0027R0: Named Types

名前付きの型を作り出す機能の提案。

typedef名は、型に別名をつけることができる。

typedef double Force ; typedef double Mass ; typedef double Accel ; Force calc_force( Mass m, Accel a ) { return m * a ; }

問題は、typedef名はあくまで別名であって、新しい型ではない。そのため、double型を渡すことができる。

void f( Object o )
{
    Mass m = o.get_mass() ;
    Accel a = o.get_accel ;

    // コンパイルエラーにならない
    Force f = calc_force( m, m ) ;
}

これを解決して、論理的に間違っている場合にコンパイルエラーを出すためには、新しい型を作り出す必要がある。

newtype double Force ;
newtype double Mass ;
newtype double Accel ;

提案では、何らかの新しいキーワード(仮にnewtype)を使って新しい型名を宣言する文法を提案している。まだ具体的な文面案がないので、どのようになるかは詳しく書かれていない。

少なくとも、暗黙の型変換は阻害されて欲しいし、テンプレート実引数で異なる特殊化をされてほしい。

提案は先行事例としてAdaを挙げている。また、Adaにはある値の範囲を定める機能: newtype Age is int(0...130)のような案も漏らしている。

P0028R0: Using non-standard attributes

attributeは、アノテーションを記述するのに便利なので、すでによく使われている。たとえば、| Reengineering and Enabling Performance and poweR of Applicationsでは、attributeを使って、GPUやDSPに処理をオフロードするアノテーションを記述できる。

問題は、inとかoutとかの短くてわかりやすい単語を使いたいが、他の実装依存のattributeとかぶるとまずいため、attribute名前空間を用いている。これは記述を煩雑にする。

void f() {
    [[ rpr::kernel, rpr::target(cpu,gpu)]]
    do_task();
}

そこで、同一のattribute内であるattribute名前空間内のattributeトークンをグローバルスコープに持ち込むための機能を提案している。

void f() {
    [[using(rpr), kernel, target(cpu,gpu)]]
    do_task();
}

P0029R0: A Unified Proposal for Composable Hashing

unorderedコンテナーのキーは、ハッシュ値を計算できる必要がある。基本型と標準ライブラリの型はstd::hashでハッシュ値が計算できる。しかし、ユーザーがハッシュ計算可能な型を組み合わせたクラスを作った場合、標準ライブラリはそのような型にハッシュ値を計算させる簡単な方法を提供していない。ユーザーが独自の方法でハッシュ値を計算しなければならない。

ハッシュ値の計算は素人には難しい。そこで、既存のハッシュ計算可能な型のオブジェクトを任意個ぶち込めばハッシュを計算してくれるライブラリhash_valueとhash_combineを提案している。

struct Foo { int i ; std::string str ; bool b ; friend std::hash_code hash_value( std::hash_code h, const Foo & foo ) ; } ; std::hash_code hash_value( std::hash_code h, const Foo & foo ) { return std::hash_combine( std::move(h), foo.i, foo.str, foo.b ) ; }

このようにすれば、Foo型はstd::hashでハッシュ計算ができるようになり、unorderdコンテナーのキーとして使うことができるようになる。

ドワンゴ広告

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

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

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

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