ISO/IEC JTC1/SC22/WG21 - Papers 2013
2013-05 post-Bristol mailingが公開された。
すでに、今回正式にドラフト入りした論文は、本の虫: Bristol会議でC++14ドラフト入りが決まった提案一覧で一部解説している。また、本の虫: C++WG 2013-03-pre-Bristol mailingの簡易レビューでも、ここで出てくる論文の改定前のものを解説している。
今回は採択された論文もあったものの、ドラフトの変更は公開されなかった。
N3624: Core Issue 1512: Pointer comparison vs qualification conversions (revision 3)
すでに何度も紹介したが、ポインターの比較やcv qualifierの変換に変更を加える提案。
この変更によって、以下のコードをill-formedにする。
void f(char * p) { if (p > 0) { ... } if (p > nullptr) { ... } }
以下のコードはwell-formedにする。
void g(int **p1, const int**p2) { if (p1 == p2) { ... } }
URIライブラリの提案のN3507からの改訂版。
リファレンス実装はcpp-netlib/uri · GitHubで公開されている。
[不潔なPDF] N3626: Floating-Point Typedefs Having Specified Widths
<cstdint>では、int16_tとかint32_tなどのように、長さが指定された整数型のtypedef名が宣言されている。この提案では、それの浮動小数点数版を提供する。
typedef名は、float16_t, float32_t, float64_t, float128_tとなり、それぞれIEEE浮動小数点数フォーマットにおける、binary16, binary32, binary64, binary128に対応する。
このtypedef名が宣言されるヘッダーであるが、従来の<cstdint>ではなく、新たに<cstdfloat>というヘッダーを設けてそこで定義することが提案されている。
この提案により、移植性の高いビット長を指定した浮動小数点数の利用が可能になる。特に、16bit長や128bit長の浮動小数点数の指定が、移植性の問題なく記述できるというのは大きい。
なお、これらはoptionalであり、このような浮動小数点数がサポートされない環境では定義されない。
N3627: Relaxed switch statement
switch文の制限を緩和して、任意の、整数型に暗黙に変換できず、constexpr operator ==を持つリテラル型を使えるようにする提案。
C++では、JavaScriptなどの他の言語のように、任意の実行時評価される式を使うわけには行かない。なぜならば、operator == が副作用を持つ場合、switch文はマッチングではなく、上から下に順番に比較して一致するものを探すという意味に変わってしまうからだ。constexpr operator ==を要求するのはそのためだ。operator ==は実行時評価することもできるが、constexpr縛りにより、副作用が禁止され、コンパイル時と実行時で評価が変わることがなくなる。
そして、string_refもしくはstring_viewのような標準ライブラリや、標準ライブラリ用のユーザー定義リテラルが採用された暁には、とてもわかり易いコードを書くことができる。
switch (s) { case "pre"s: // do something case "prefix"s: // do more things break; }
とか、
switch (e /* std::complex<double>, or std::array<double, 2> */) { case { 1.0, 3.0 }: break; }
といったぐらいだ。二番目の例は、ユーザー定義リテラルにより、caseラベルがさらにわかりやすくなる。
switch (e) { case 1.0+3.0i: break; }
リテラル型で、constexpr operator ==を持ち、暗黙に整数型に変換できないクラス型というかなり限定された条件ではあるものの、かなり便利になる。
ただし、その制限から、普通の人間が使えるような機能ではない。
[不条理なPDF] N3628: C and C++ Compatibility
Bjarne Stroustrupが10年以上前に書いて2002年に公開したCとC++の互換性に関する記事三本をまとめてC++WGの論文として公開。D&Eが好きな人は読んでみるといい。特に、三本目の記事は、2002年当時のBjarne Stroustrupの考え方が現れていて面白い。nullを表すキーワードnullptrはC++11でめでたく採用された。
この文章の執筆から10年以上たっているわけだが、CとC++の溝は深まるばかりだ。
[PDFが許されるのはARM C++までだよねーキモーイ] N3629: Simplifying C++0x Concepts
C++11に入ることが予定されていたFull Conceptと比較して、今提案中のConcept Liteではどのような制限を受けるのかという比較論文。
Concept Liteはテンプレートの実体化元の型が当てはまるかどうかを検証するだけで、テンプレート定義を検証しない。Concept LiteにはConcept Map機能がない。コンセプトのrefinement(コンセプト版派生)がない。axiomがない。コンセプトがデフォルトで暗黙になる。といったところだ。
axiomだけは全然惜しくない。
ところで当時、テンプレートの定義の検証方法について、archetypeという画期的な方法が考案されていたようだ。
A Breakthrough for Concepts « C++Next
単にコンセプトで指定されているだけのことができるクラスを精製して、テンプレートのインスタンス化に成功するかどうかを調べれば良い。こんな裏ワザ的な方法でテンプレート定義のコンセプト検証ができるとは目からウロコだ。
ConceptGCCは糞真面目に検証しようとしてバグだらけの不完全な実装に成り下がっていた。
このarchetype実装では、テンプレートのインスタンス化無失敗保証はできないものの、ほとんどのケースはカバーできるという。何より既存のインスタンス化の仕組みを使うので手軽だ。なんだか、テンプレートの実装方法としてinclusion modelが発明されたのと同じ臭いがする。
このarchetype技法はBoost.ConceptCheckに使われているものと同じらしい。
archetype技法は他のメタプログラミング技法と同じで、変態的な裏ワザだが、面白いことは確かだ。
[PDFは邪道。これは定説] N3630: async, ~future, and ~thread (Revision 1)
基本的に、futureのデストラクターはブロックしないが、std::asyncから返されたfutureのデストラクターだけは特別にブロックする。これは非常に不思議な挙動を引き起こす。
例えば、以下のコードと、
{ std::async([]{ f() ; } ) ; std::async([]{ g() ; } ) ; }
以下のコードは、
{ auto f1 = std::async([]{ f() ; } ) ; auto f2 = std::async([]{ g() ; } ) ; }
異なる意味を持つ。前者には同期が発生するが、後者には同期が発生しない。
これは、デストラクターがブロックするため、前者のコードは、もし実装はlaunch:asyncを選んだ場合、二つ目のasyncが実行される前に、一つ目のasyncの終了に同期される。
つまりはこういうことだ。
{ std::async([]{ f() ; } ) ; // 一時変数のfutureのデストラクターが実行されるためブロック std::async([]{ g() ; } ) ; } { auto f1 = std::async([]{ f() ; } ) ; // ブロックしない auto f2 = std::async([]{ g() ; } ) ; // ここで同期 }
さらに、以下の一見ブロックしないようにみえるコード
{ std::async( std::launch::async, []{ f() ; } ) ; // ブロック std::async( std::launch::async, []{ g() ; } ) ; }
このようなコードでは、ユーザーは戻り値には興味がなく、単に立ちあげて忘れるという意味で書いたのに、二つ目のstd::asyncを呼び出す前にブロックしてしまう。
さらに難しいのはこのコード
void func() { std::future<int> f = start_some_work() ; // その他の処理 // futureのデストラクターはここでブロックするのかしないのか不明 }
もし、start_some_workの実装が、std::asyncを使っていたならば、futureのですトラクターはブロックする。もし、std::threadを使っていたならばブロックしない。そのため、futureがブロックするかどうか分からない。
このため、既存の挙動を変えて、std::asyncでもブロックしないようにする提案がなされている。
この提案が受け入れられない場合、このことをC++プログラマーに教育しなければならない。しかも、将来は絶対ブロックしない別バージョンのfutureを重複して取り入れることになる。
かわりに、デストラクターでのブロックを保証するlocal_futureを付け加える提案もされている。
また、現在のところ、std::threadのデストラクターは、joinもdetachもされていない場合、std::terminateを呼び出す。一貫性を保つため、std::threadのデストラクターはjoinするようにしてはどうかという提案もされている。やはり、Boostとは違い、C++ではthreadのデストラクターがjoinせずにstd::terminateを呼び出すのは違和感があったので、これはいい変更だと思う。
[クサレPDF] N3631: C11: The New C Standard
Dr. Dobb’sで連載されているC11紹介記事を三本、C++委員に紹介するためにコピー。C++委員以外は本家Webサイトを参照してくれとのこと。
fopen_sって本当にセキュリティだろうか。fopen_sは、uを使わない場合、ファイルを排他的に開く。fopen_sで開かれたファイルは他のプロセスから開くことができない。
Windowsがネイティブに提供しているファイルの排他的なオープンは、Unixの世界ではまれだ。Linuxでは原則として、ファイルロックはadvisoryに行われる。ファイルロックしたい場合openにO_EXCLを指定する。ただし、他のプロセスがO_EXCLを付けずにopenを呼び出すことは阻止できないし、その場合は普通にファイルが開かれる。
ひとつのプロセスに排他的なファイルロックは果たして正しいセキュリティ機能だろうか
一応、Linux独自の実装として、flockでそのようなプロセス排他のファイルロックは実現できるし、他のOSでも、flockやらlockfやらfcntl経由やらで提供されているが、はたしてセキュリティだろうか。
まあ、Cには興味がない。
N3632: Additional std::async Launch Policies
std::asyncに新しいランチポリシーを3つ付け加える提案。
launch::task
これはlaunch::asyncに似ていて非同期だが、スレッドの再利用を許すものである。launch::asyncは、あたかも新しいスレッドで実行されたかのように振る舞うと定義されているが、これはつまり、実際に新しくスレッドを作らなければならない。既存の別のlaunch::async用に作ったスレッドを使いまわすことはできない。新しくスレッドを作ることの利点は、スレッドローカルストレージも新しく確保されるという事で、これはある意味で好ましいかも知れないが、パフォーマンス上問題である。スレッドローカルストレージが以前のものを再利用してもかまわないのであれば、スレッドを使いまわせるlaunch::taskはパフォーマンス上有利である。
launch::sync
これは同期で、しかもstd::asyncの中で実行するランチポリシー。つまり、std::asyncを経由せずその場で直接呼び出すのと何ら変わらない。
これは一見無意味なように思えるが、何らかの目的で同期呼び出しに切り替えたい場合も、別にコードを書くことなく切り替えることができる。たとえば、ポリシーを受け取ってstd::asyncを呼び出すラッパーというのも現実的にありうる。
void routine( std::launch policy, args...) { /* ... */ std::future<X> fx = std::async( policy, ... ); /* ... */ }
このようなコードが、もしstd::launch::syncをサポートしていない場合、とても面倒になる。まずstd::launchではなく独自のenumを使わなければならないだろうし、std::asyncを呼び出すか直接呼び出すかの条件分岐も、自前で書かなければならなくなる。
launch::default_
ポリシーを指定しない場合のデフォルトの挙動を明示的に指定するためのポリシー。この値は、sync | async | task | deferredになる。
この提案の結果、デフォルトのポリシーはlaunch::syncも含むことになるが、これは問題ではないだろうか。たとえば
auto f = std::async( []{ /* ブロックする処理 */ } ) ;
このコードは、現行規格では絶対にブロックしない。どちらのlaunch::asyncとlaunch::deferredのどちらのポリシーが選ばれようと、std::async呼び出し自体はブロックしない。しかし、もしN3632が採用されたら、ブロックするかもしれないのだ。
もし、将来の規格が新たに異質なランチポリシーを追加し、デフォルトポリシーが追加された新ポリシーを含むとしたら、デフォルトポリシーを安全に使うことは到底できない。何故こんな明白に危険な提案が出されたのか。
この疑問を著者のPeter Dimovにぶつけてみたところ、
基本的には正しい。しかし、今この時点では、既存のコードへの影響はほぼない。このあとの将来の規格ではデフォルトをいじることはできないだろう
今回、デフォルトを拡張する提案を盛り込んだのは、通る見込みが薄いとしても、今が変更できる最後の機会だからだ。
もちろん、N3632は退けられたので、何の意味もないけど。
とのことだった。たしかに、まだstd::asyncを使った現場で動いている既存のコードは少ないだろう。また、Bristol会議のコンセンサスは、「asyncのランチポリシーを拡張するのではなく、executerライブラリに力を入れるべきだ」とのことだそうだ。
まあ、よくも悪くもstd::asyncはお手軽ライブラリで、柔軟な利用には向いている設計ではない。
N3633: What can signal handlers do? (CWG 1441)
シグナルハンドラーの制限を緩和する提案。シグナルハンドラーの中で普通にローカル変数やアトミックが使えるようになる。
[スカラー電磁波とPDFは人体にとって有害である] N3634: Improvements to std::future<T> and Related APIs
N3558の改訂版。具体的な文面案も盛り込まれた。本の虫: C++WG 2013-03-pre-Bristol mailingの簡易レビューを参照。
[光を放ち今立ち上がる若きPDFに帰依しよう] N3635: Towards restrict-like semantics for C++
restrictをC++でサポートするための考察論文。restrictの利点は疑う余地がないとしながら、C++に導入するのはなかなか難しい。
[もし妻がPDFを使っていた場合、石もて打ち殺さねばならない。申命記 22:13-21] N3636: ~thread should join
N3630からthreadのデストラクターだけに注目した論文。現在、joinもdetachもされていないthreadのオブジェクトのデストラクターが呼ばれた場合、std::terminateが呼ばれる。Boostではjoinが呼ばれたのだが、標準ライブラリに取り入れる際に、明示的にjoinもdetachもされていないスレッドはバグの元だからstd::terminateを呼ぼうという合意に達した。
やっぱり変えようということで、threadのデストラクターはjoinableならjoinを呼び、そうでなければなにもしないようになる。
というかなんで最初からBoostの挙動にしておかなかったのか。
[PDFを信ずる者らを恐怖せしめよ。そのため、彼らの頭を叩き斬り、指をすべて叩き切れ。クルアーン 8:12] N3637: async and ~future (Revision 3)
デストラクターの挙動の違うfutureを追加しようという提案。
既存のfutureとshared_futureは、デストラクターが決してwaitもgetもしないようにする。そして、デストラクターがwaitかgetするwaiting_future/shared_waiting_futureを追加する。
N3638: Return type deduction for normal functions
関数の戻り値の型推定がC++14に入る。まあ、解説の必要もないほど普通の型推論だ。
// auto func() -> int と同じ auto func() { return 0 ; }
これはK&R Cにあった暗黙のintのような邪悪な機能ではない。たとえば以下のようなコードは正しくエラーとなる。
// エラー、戻り値の型を型推論できない。 auto func( bool b ) { if ( b ) return 0 ; else return 0.0 ; }
再帰も可能だ。
auto power( unsigned int x, unsigned int y ) { if ( y == 0 ) return 1 ; else return x * power( x, y-1 ) ; }
再帰の場合も、正しく型推定できるし、すべてのreturn文が同じ型を返せば合法となる。
戻り値の型を指定しない定義ではない関数の宣言もできる。これは、クラスの定義でメンバー関数を宣言だけしたい場合、以下のように書きたいし、
struct A { auto f(); // forward declaration }; auto A::f() { return 42; }
これを許すならば、一貫性を保つため、他の場面でも宣言できるべきであるからだ。
C++14では、推定される戻り値の型に言及しない場合のみ、宣言ができる。
auto f(); // return type is unknown auto f() { return 42; } // return type is int auto f(); // redeclaration int f(); // error, declares a different function
テンプレートの場合も同様。
template <class T> auto g(T t); // forward declaration template <class T> auto g(T t) { return t; } // return type is deduced at instantiation time template <class T> auto g(T t); // redeclaration
もちろん、このような場合に、宣言だけして定義が事前に見えない関数を使うとエラーとなる。
auto f(); // return type is unknown int i = f(); // error, return type of f is unknown
テンプレートの明示的特殊化や明示的実体化も、元のテンプレートがautoを使っていた場合、autoを使う必要がある。
template <class T> auto f(T t) { return t; } // #1 template auto f(int); // OK template char f(char); // error, no matching template template<> auto f(double); // OK, forward declaration with unknown return type template <class T> T f(T t) { return t; } // OK, not functionally equivalent to #1 template char f(char); // OK, now there is a matching template template auto f(float); // OK, matches #1
戻り値の推定にテンプレートの実体化が関わってくる関係上、SFINAEかどうかという扱いが気になる。テンプレートの実体化がill-formedの場合は、substitution failureではなく、エラーになる。
auto関数を使うと、lambda式をそのまま返すこともできる。
auto f() { int value = 0 ; return [=]{ return value ; } ; }
C++11ではクロージャーオブジェクトの型を指定する方法がないため、lambda式によって生成されるクロージャーオブジェクトを直接返すことができなかったのだが、C++14では可能になる。
std::initializer_listを推定することはできない。virtual関数は技術上不可能ではないが、実装がややこしくなるため禁止されている。
N3639: Runtime-sized arrays with automatic storage duration (revision 4)
実行時にサイズを指定できる配列の提案。C99にある機能だ。
N3640: Extending shared_ptr to Support Arrays
shared_ptrを配列に対応させる提案。
N3641: Extending make_shared to Support Arrays
make_sharedを配列に対応させる提案。
[ド低脳PDF] N3642: User-defined Literals for Standard Library Types (part 1 - version 4)
標準ライブラリをユーザー定義リテラルに対応させる提案。std::complexは別論文に分割。
[くそったれPDF] N3643: Range Adaptor for Selecting from Pair or Tuple
pairやtupleを返すイテレーターに対して、何番目かの要素を指定して返すイテレーターアダプターを返すレンジアダプターの提案。
for (auto& i : m) { i.second.foo() ; i.second.bar() ; }
と書く代わりに、
for (auto& i : select<1>(m)) { i.foo(); i.bar(); }
と書くことができる。
[ボケナスPDF] N3644: Null Forward Iterators
要素を刺さず、コンテナーのオブジェクトにも関連付けられていない、nullイテレーターの提案。
[存在時代が冗談みたいなPDF] N3645: Splicing Maps and Sets (Revision 1)
従来のlistで提供されている互換アロケーター同士のlistのオブジェクトで、内部のストレージ上に構築した要素を直接移動するspliceを、mapとsetとunordered_mapとunordered_setにも提供する提案。
mapやsetの特性に合わせた設計になっている。
[わらうべきPDF] N3646: Network byte order conversion
今となっては古典的なバイトオーダー変換ライブラリの提案。テンプレートによるジェネリックな実装の追加の他、互換性維持のために、htonl, htons, ntohl, ntohsも提供する。ヘッダー名<net>が提案されている。
個人的な意見では、この2013年に、ネットワーク越しにバイトオーダーが問題になるようなプロトコルを使うべきではないと思う。UTF-8か、ビット列を使うべきだ。
N3648: Wording Changes for Generalized Lambda-capture
Generalized Lamda-captureの文面案。このgeneralizedとは、汎用ラムダキャプチャー、あるいは一般ラムダキャプチャーなどと訳すべき機能で、ラムダキャプチャーで識別子と初期化子からなる文法を使い、キャプチャーの方法を明示的に指定できる機能だ。
void f() { int x = 1 ; // [x] と同じ [ x = x ] { return x ; }() ; }
汎用ラムダキャプチャーは、例えば変数を別名でキャプチャーすることができる。
void f() { int x = 0 ; // xをyという名前でコピーによるキャプチャ [ y = x ] { } ; // xをyという名前でリファレンスによるキャプチャ [ &y = x ] { } ; }
初期化子は、他の初期化子と何ら変わりなく使える。
void f() { int x = 0 ; // xに1を足した値をxという名前でコピーによるキャプチャ [ x = x + 1 ] { } ; // x == 0 }
汎用ラムダキャプチャーは代入ではないことに注意。あくまでキャプチャーの方法を指定するだけである。
汎用ラムダキャプチャーに難でも書ける初期化子がある最大の理由は、ムーブキャプチャーを書けるようにするためだ。
void f() { std::vector<int> v ; // vをvという名前でムーブしてコピーによるキャプチャー [ v = std::move(v)] { } ; // ここでvはムーブされている }
また、クラスのメンバーをコピーキャプチャーすることもできる。
struct X { int member ; void f() { // エラー、変数名memberは存在しない [member] { } ; // OK、ただしmemberはthis経由のリファレンスによるキャプチャー [=]{ return member ; } ; // memberをmemberとしてコピーによるキャプチャー [ member{member}] { return member ; } ; } } ;
文法は以下の通り
init_capture: identifier initializer & identifier initializer
初期化子なので、[x = x]のかわりに、[x{x}]といった記述も可能だ。識別子の前に&がつくのは、ムーブによるキャプチャーだ。
文法上は、init-captureとなっている。初期化キャプチャーとでも呼ぶべきなんだろうか。
Generalized Lamda-captureにより、ラムダキャプチャーを一般的な初期化子として記述できるようになる。
この提案はぜひとも入って欲しい。
N3649 Wording for Auto Generic Lambda Proposal (Bristol 2013)
autoジェネリックlambdaの文面案。ポリモーフィックラムダとも言う。ポリモーフィックといっても、静的なポリモルフィズムである。こっちのジェネリックは、型をパラメーター化する方のジェネリックで、つまりはテンプレートと同じだ。
メンバーテンプレートを使った以下のような関数オブジェクトを、lambda式で書けるようになる。
struct Function_object { template < typename T > T operator () const ( T t ) { return t ; } } ;
この提案による同等のlambda式は以下の通り。
[]( auto t ) { return t ; } ;
はよ入れはよ。
[クソ、またPDFか] N3650: Resumable Functions
レジューム可能な関数の提案。この提案はN3634と対になっている。N3634はライブラリベースの非同期処理の提案で、こちらは非同期処理をコア言語で記述できるようになる提案だ。
なぜライブラリベースではだめなのかというと、ライブラリベースで非同期処理を実装すると、以下のようなコードになるからだ。
future<int> f(shared_ptr<stream> str) { shared_ptr<vector<char>> buf = ...; return str->read(512, buf) .then([](future<int> op) { // lambda 1return op.get() + 11; }); } future<void> g() { shared_ptr<stream> s = ...; return f(s).then([s](future<int> op) { s->close(); }); }
このように、とてつもなく読みづらい。あらゆる非同期処理はクラスのオブジェクトを返し、そのオブジェクトのメンバーthenを呼び出して、処理が終了した後の処理を渡す。これはlambdaで可能だ。こうすることによって、非同期処理がライブラリベースで記述できる。Windows RTの設計もこのようになっていて、ファイルひとつ読み書きするにも非同期処理を要求し、しかもとてつもなく制限された実行環境で、ライブラリを作成して配布ということもできないため、Windows RTのプログラマーは皆、自前でライブラリを作るか、諦めてWindows RTを直接使うかの選択を余儀なくされるという非常にクソなことになっている。
このコードは、コア言語が途中で中断して後で再開可能な関数、すなわちResumable functionを提供していれば、とても読みやすくなる。同等のコードは以下のように書ける。
future<int> f(stream str) async { shared_ptr<vector<char>> buf = ...; int count = await str.read(512, buf); return count + 11; } future<void> g() async { stream s = ...; int pls11 = await f(s); s.close(); }
わざわざラムダを入れ子にするよりよっぽど読みやすい。futureを返す関数にasyncキーワードを使って、resumable functionであることを指定し、awaitキーワードで、それ以降の処理が非同期に実行されることを指定する。
関数の呼び出し元からみると、awaitキーワードが実行された時点で、関数は処理から戻ったようにみえる。もちろん、実際には実行が中断されているだけだ。結果の戻り値を確認するにはfutureのgetを使わなければならない。futureのgetを使うと、関数の実行が中断された箇所から再開される。実行の再開は、別のスレッドで行うこともできる。
ライブラリベースの非同期処理では、このawait以降に当たる処理を、関数の入れ子にしなければならない。さらに、メモリは動的に確保しなければならない。Resumable関数では、実装がスタックなどのメモリを動的に確保するので、普通の自動変数が使える。
[PDFは死滅せよ] N3651: Variable Templates (Revision 1)
変数テンプレートの提案。変数宣言をテンプレート宣言できるようにしただけで、文面上はそれほど大きな変更ではない。ユーザーが見るコードとしては、相当な変化があるが。
N3652: Relaxing constraints on constexpr functions
constexpr関数の制限を大幅に緩和する提案。議論の結果、以下の制限緩和の合意に至った。
constexpr関数の中での変数宣言。ただし、staticとthread_local、未初期化の変数宣言を除く。
constexpr int ok( int x ) { int temp = x ; return temp ; } constexpr int error() { static int a = 0 ; // エラー thread_local int b = 0 ; // エラー int c ; // エラー、未初期化 return 0 ; }
ifとswitch文の許可(gotoは禁止)
constexpr bool Not( bool b ) { if ( b ) return false ; else return true ; } constexpr bool prime_table( unsigned int value ) { switch( value ) { case 2 : return true ; case 3 : return true ; case 5 : return true ; default : return false ; } }
これで条件分岐をまともな文として書ける。
寿命が定数式評価時に始まるオブジェクトの変更。
constexpr int f( int x ) { int temp = 0 ; temp = x * x ; return temp ; }
ループ文をすべて許可:for(Range-based forも含む), while, do-while
constexpr unsigned int power( unsigned int x, unsigned int y ) { unsigned int temp = 1 ; for ( ; y != 0 ; --y ) { temp *= x ; } return temp ; }
これでループを再帰で書かなくてもすむ。
また議論の結果、constexpr非staticメンバー関数は暗黙のconstではなくなった。よくぞ決断した。
N3653: Member initializers and aggregates
メンバー初期化子とaggregate初期化を組み合わせて使えるよう制限を緩和する変更の文面案。
制限緩和の背景について詳しくはN3605: Member initializers and aggregatesを参照。
区切り文字を含む文字列をそのままストリームで扱うマニピュレーターの提案。
[PDFは絶滅しろ] N3655: TransformationTraits Redux, v2
メタ関数のエイリアステンプレートによるラッパーを追加。いちいちメタ関数の戻り値を得るためにtypeという名前のnested typeを書かなくて良くなる。ラップする名前はmetafunctionという名前のメタ関数に対して、metafunction_tとなる。
[preを利用した実質プレインテキストなHTML] N3656: make_unique (Revision 1)
make_sharedのunique_ptr版であるmake_uniqueの提案。
この論文は[PDF注意] N3465の改訂版である。
C++11では、Dave Abrahamsの論文、Binary Search with Heterogeneous Comparisonのalgorithmによる実装が採用された。具体的には規格を参照してもらうとして、そのheterogeneous compparisonを連想コンテナにも広げる提案。
N3658: Compile-time integer sequences
テンプレートパラメーターパックで使える連番の整数列を生成するライブラリの提案。議論の結果、名前を変更が行われている。また、効率に対する考察が追加されている。これは本の虫: std::make_integer_seqをO(LogN)にしろという議論でボレロ村上さんが問題提起した結果だろう。
効率の考察
N個の整数のシークエンスにおけるリファレンス実装は、N個テンプレートを再帰的に実体化するので、シークエンスの長さはコンパイラーのインスタンス化の深さの最大長に制限されてしまう。シーケンスの両端から開始して、中央に向かって同時に生成することで、インスタンス化の回数をlog2(N)に低減させることは簡単にできる。
コンパイラーintrinsicを使って、再帰なしで任意長の整数シークエンスを実体化することも可能である。
コア言語機能として、...3を{ 0, 1, 2, 3}のようなパラメーターパックに展開し、この問題を解決する別の方法も提案された。この言語機能はC++14の段階では採用される望みはないため、この問題を解決する方法として本論文の提案の採用を妨げるものではない。
うむ、そのコア言語機能は欲しい。
詳しくは本の虫: C++WG 2013-03-pre-Bristol mailingの簡易レビューのN3554の解説を参照。
N3659: Shared Locking Revision 2
multiple-readers / single-writer locking patternを実装するためのライブラリの提案。
std::complexをユーザー定義リテラルに対応させる提案。他の標準ライブラリのユーザー定義リテラルを追加する提案とは分割された。
数値リテラルを区切り文字を使って読みやすくする提案。
問題なのは、その区切り文字を何にするかという事だ。コンマは明白な理由で使えない。今のところ一番人気なのはアンダースコアだが、これも色々と問題が多い。現行の提案論文では、アンダースコアになっている。
実行時に長さを指定できる配列ライブラリDynarrayの提案。コア言語でC99の動的配列をサポートするよりライブラリベースで提供すべきだと主張。
ストレージのサイズを実引数として取るdeallocation functionのオーバーロードを追加する提案。
N3664: Clarifying Memory Allocation
規格のメモリ確保の文面を厳密に解釈すると確保要求のたびに逐一確保関数を呼びださなければならず、また実行時の前後のメモリ確保の状況で挙動を変えてはいけないようにも読める。そのような無意味な制限の撤廃。
N3665: Uninterleaved String Output Streaming
同期してストリームに出力した際、十分に小さな文字列では、出力が途中で分断されないよう保証する提案。
N3666: C++ Latches and Barriers
処理が完了するまでスレッドをブロックするライブラリ、ラッチとバリアーの提案。
ムーブコンストラクターとムーブ代入演算子があまりに頻繁にdeleted定義されるので、条件の緩和の提案。
N3668: exchange() utility function, revision 3
非アトミック版の関数テンプレートexchange()の提案。
[いかなる時でも自転車が壊れたりPDFがはびこれば自転車修理マンは行く] N3669: Fixing constexpr member functions without const
constexpr非staticメンバー関数を暗黙のconstにしない変更の文面案。
N3670: Wording for Addressing Tuples by Type: Revision 2
tupleで重複しない型の要素に、まるで連想コンテナのように型でアクセスできるよう変更する提案の文面案。
N3671: Making non-modifying sequence operations more robust: Revision 2
ひとつのRangeの結果を、別のイテレーターに書き込むアルゴリズムは、出力用の別のイテレーターは開閉の対で取らない。そのため、些細なプログラミング上のミスにより、Rangeの範囲を超えて書き込むということが起こりうる。
そのような既存のアルゴリズムに、出力用のイテレーターも開閉の対で取るオーバーロードを追加する提案の文面案。
というかこのセキュリティが重要な時代に既存の設計はおかしい。
N3672: A proposal to add a utility class to represent optional objects (Revision 4)
ライブラリstd::optionalの文面案。
N3677: A Proposal to Add additional RAII Wrappers to the Standard Library
そうだよ。こーゆーのがほしかったんだよ。
ほとんどの論文がすでに存在する論文の改訂版や文面案だという会議後の論文の中で、これは珍しく新しい論文。
ジェネリックなRAIIラッパーライブラリを追加する提案。unique_ptrをさらに汎用的にしたものだと言える。
独自の簡単な解放処理が必要なリソースや処理に対して、わざわざ自前でクラスを書くことなく、このライブラリで済ませられる。
スコープから抜ける際に条件を満たすなどすれば登録された処理を行うscoped_function, scoped_resource_unchecked, scoped_resource, unique_resourceを追加する。
これはぜひとも欲しい。
ストリームを排他的にロックして操作できるライブラリの提案。
N3679: Async future destructors must wait
すんでのところで採択されかけた危険な変更、std::asyncが返すfutureのデストラクターがブロックするのをやめるという変更が、なぜ問題なのかということを解説した論文。
dangling referenceの問題は通常のlambdaにも存在するが、std::asyncを使うとすでに実行されている分、なおさら悪いという事だ。
N3680: Improving Pair and Tuple
現行規格では、pairやtupleを使った以下のコードが動かない。
std::tuple<int, int> pixel_coordinates() { return {10, -15}; // Oops: Error } struct NonCopyable { NonCopyable(int); NonCopyable(const NonCopyable&) = delete; }; std::pair<NonCopyable, double> pmd{42, 3.14}; // Oops: Error
何でダメなんだよ? おかしいだろ? ジョーシキで考えてフツーに動けよ? という疑問に答えるため、何故これが動かないのかという解説と歴史的経緯の説明、そして解決法を提案している。
詳しい理由は論文を読んでもらうとして、暗黙の型変換による些細なプログラミングミスを検出するために、当時はあえてこのような設計にしたのだという。
解決法は、perfect initializationというテクニックを用いる。ほぼ同じメタ関数でガードした非explicitとexplicitなコンストラクターを宣言すると、なかなか興味深いことに、暗黙の型変換を行うdirect initializationは許可するが、暗黙の型変換を行うcopy initializationは許可しないという挙動になるらしい。
それから、"This function does not participate in overload resolution unless […]"という文面は、SFINAEで除去するのではなく、実際にその条件で明示的特殊化をするが失敗するよう定義してもいいのだという。なるほど、そんな手もあったか。
色々と技法の勉強になる。
N3681: Auto and braced-init-lists
auto指定子がbraced initializerからinitializer listを推定してしまうのは問題が多いから禁止しようという提案。
具体的には、auto指定子がinitializer listを推定しないようにし、また複数の要素をもつbraced initializerをautoで受けるのも禁止する。
これは、互換性をぶち壊す変更だ。
// C++11では、decltype(a)はstd::initializer_list<int> // N3681では、エラーとなる auto a = { 1, 2 } ;
ただ、このようなコードはまず書かれないだろうから、おそらく問題はないだろう。
N3685: string_view: a non-owning reference to a string, revision 4
文字列を様々な内部表現へのリファレンスで表現して、共通のインターフェースを提供するstring_viewライブラリとその文面案。
N3686: Traversable arguments for container constructors and methods, wording revision 3
従来、Rangeと呼ばれていたコンセプトの提案。ただし、Rangeという名前は意味が重複しており、誤解を招きやすい。そこで、投票の結果、Traversableという名前に改名することに決定した。
しかし、すでにRange-based forという正式な名称があるのだが、ややこしい。
実装は、Range-based forと同じくADLベースになる。
No comments:
Post a Comment