2015-10-20

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

12 comments:

Anonymous said...

レンジ関係がちょっと面白そうですね。
ユーザーがカスタムの範囲を記述できるように内容的仕様も明示してほしいかな。
それと、地味にNewTypeが気になってます。
ユーザー定義リテラルと合わせることで現実にあるメトリクスの相関関係を記述できるようになるんじゃないかって期待です。
何らかのいいところに落ちてほしいですね。
クランプが地味に議論続いてますね。もう仕様に入ったもんだと思ってましたがまだやってましたか。現在の引数列は割と好みです。
パラレリズム関係は、さっさと標準の記述方法を確立してほしいですかね。ミューテックスやロック関係と合わせてうまく落としてほしいです。
イデオンなどの環境でスレッド立てるの大変なのでもうちょっと簡単になってほしい。
例えば、スレッドを立てつつさらにレンジで区切って区間をSIMDで処理とかも普通にあると思うのでそれらが簡単に立てれればいいですね。

Anonymous said...

> メトリクスの相関関係
そのソリューションは非現実的ですね。

Anonymous said...

なぜですか?
Pound P = 65kg;
これでいろんな変換がすんでしまいます。
本来必要とされる数学や科学方面に強くなると思いますし、
プログラムになじみのない研究者程便利になると思うのですが、
なぜ非現実的に至ったのか自分には詠むことができませんでした。
具体的な反証をお願いしてもいいですか?

jaguar said...

単位ごとに全部別の型にして演算子を全部オーバーロードするつもりなんですか?
少しは頭を使いましょうよ。

Anonymous said...

そんな馬鹿な話ですかねぇ。
現実的にそれだけの単位が存在するからリアルを記述したい欲求なんですが。
一回書けばメンテを除いて得られる恩恵は大きいと思いますがねぇ。
単位とはつまり型とほとんど同義だと思いますよ。

Anonymous said...

小学校の算数の宿題ならそれでもいいかもしれませんね。
boost.unitsとか調べてみましょうや。どう違うかあなたに分かるでしょうか。
自分で丸出しって分からないようじゃ不安ですけど。

Anonymous said...

狭いですねぇ。そんなに躍起になって可能性をつぶすのが流行りなんですか?
数学や物理はもっと一般的になるべきなんです。簡単になるべきなんです。
じゃないとエンターテインメントは死んでしまいます。
そのためのツールキットとしてのnewtypeに期待したかったのですが。
どうにも話が通じないですねぇ。

まぁ、必要な人が必要な変換を書いて収集できればいいなー程度に思っています。

ちなみにバカは美徳ですよ。
賢者は頭で考えて手を出さない。
それでは世界は開発できない。

Anonymous said...

だってあなた実際手を動かしてないじゃないですかw
((基本の型の数+1)×単位の数)!×演算子の数のオーバーロードが必要なんですけど、
あなた、ちょっとでも考えました?
複素数やら多倍長実数やら、新しい型も増える。単位の組み合わせは無限で、
一個追加するたびにオーバーロードは幾何級数的に増えていく。
何年も前にみんなが捨てた可能性なんです。
あなた、boostにある先行事例すら見つけられなかったでしょう。
あなたはバカの名にすら値しないようですね。自白をありがとうございます。

江添さんの、元論文に無い奇怪な用例でっち上げの被害者というべきでしょうかw

yumetodo said...

>((基本の型の数+1)×単位の数)!×演算子の数のオーバーロードが必要なんですけど、

横からの素人意見ですみませんが、SI単位のみ用意してその他単位はSI単位への型変換演算子とSI単位を受けるコンストラクタがあればそこまでパターンは増えないと思うんですが(それにしても多いけど)、何を私は見落としているんだろう・・・

Anonymous said...

2つ上の人は話にならないですね。
手の抜き方を知らないようだ。
型の数全部網羅して何に使うんですか。
基本的にdoubleや多倍長精度実数があれば必要十分でしょう。
型の数は4つもあれば実用上の問題はほとんどないでしょう。
それと定義されていない演算も無数にあるのでその数は正しくない。

俺言いましたよねぇ。
必要な人が必要な変換を書く。一回書けば受けられる恩恵は大きいと。
非正規な演算については普通にstatic_castすればいいの級数的な数にはならないはずです。

この人、何と戦ってるのかよくわからないです。

Anonymous said...

NewTypeは是非とも実現して欲しいですね。
アプリケーションハンガリアンでやろうとしていたことがコンパイラに任せられるので。

Anonymous said...

>基本的にdoubleや多倍長精度実数があれば必要十分
それが罷り通るなら、C++にも、doubleと多倍長精度実数があれば必要十分という事になりませんか?
そして「必要な人が必要な変換を書く」で済ませられる話なら、標準ライブラリに入れなくていいんです、必要な人が書いてライブラリ公開すればいいんですから。
「必要な人が必要な変換を書かなくて済む」ようにあるのが標準ライブラリでしょう。

この辺、ユーザー定義リテラルの合成機能があれば楽なんですけどねぇ……。
「X operator "" _g(unsigned long long int ll);」と「template<class T> Y<T> operator "" _k*(const T& t);」が定義されていれば、暗黙の「Y<X> operator "" _k_g(unsigned long long int ll)」が定義される、みたいな。
_gも「template <class U> X<U> operator "" _g*(const U& u);」にしておけば、組み込みのプレフィックスを使って「X<long> operator "" _gl(long l);」「X<float> operator "" _gf(float f);」……が定義できそうで便利。