2012-09 pre-Portland mailingが公開された。久しぶりなのですっかり忘れていた。今回はやたらと多い。もちろん、もはや正式規格発行後なので、差し迫った変更はない。細部の疑わしい文面の変更とか、大雑把な提案とか、将来C++への追加の可能性のある機能が、現状ではどのように独自な拡張やライブラリで実装されているのかなどの紹介といったところだ。今回は、紹介も非常に多く、規格への提案というよりも、C++11でライブラリはどのようになるかといった紹介が多いように思う。
とにかく今回は数が多い。疲れた。非常に疲れた。無償でやるのも不毛だ。
C++11は、2000年中に制定されることが期待されていたので、だいぶ最近までC++0xと呼ばれていた。C++0xという呼称を最初に使ったのは他ならぬBjarne Stroustrupだが、次の規格の呼称もすでに生まれている。C++1yという。現在のところ、2017年の制定を目標にしているが、まあおそらく、あと5年では無理ではないかと思う。
N3386: Return type deduction for normal functions
通常の関数で、戻り値の型の指定を省略して、推定させるもの。すでに、lambda expressionではこれが可能である。C++11でも、せっかくlambda expressionはそうなっているのだし、新しい関数の宣言文法も導入したのだから、通常の関数でも認めればどうかという話は当然出るわけで、実際に議論されたのだが、時間がないので、将来の規格改訂に先送りすることにしたのだ。
このたび、GCC開発者のJason MerrillがGCC上で実験的な実装を行い、C++1yに追加するだけの十分な実装経験が得られたとして、その経過や所感を報告している。
まず問題になるのは、前方宣言だ。戻り値の型を省略した前方宣言は認められるべきだろうか。
auto f() ; // 戻り値の型はint auto f() { return 42 ; }
これはC言語の汚らしい仕様である、暗黙のintを思い起こさせるが、一方、クラスのメンバー関数の定義を外部に書きたい場合、これが許されてくれないと困る。
struct X { auto f() ; } ; auto X::f() { return 42 ; }
これは当然書きたい。もしこれを言語として許すのであれば、他の場所でも当然、戻り値の型を省略した前方宣言が許されるべきである。しかしAlisdairが言うように、問題は、これをSFINAEに持ち込むと、普通のユーザーにとっては、非常に奇妙な結果になるという事だ。
auto f(int) ; template <class T, int = sizeof(f(T()))> void g(); void h1() { g<int>(); } // no match, g() disqualified by SFINAE auto f(int i) { return i; } void h2() { g<int>(); } // OK
しかし、Merrillによれば、すでにincomplete class typeを使えば同じ状況にはなるわけで、いまさらどうということはないとのことだ。
Jason Merrillの今回の提案では、戻り値の型を省略した関数の宣言を許そうではないか。ただし、すべての宣言が全く同じ型であればだ。
auto f(); // return type is unknown auto f() { return 42; } // return type is int auto f(); // redeclaration int f(); // error, declares a different function
もちろん、定義が現れる前にそのような関数をexpressionの中で使った場合、ill-formedである。
auto f() ; int i = f() ; // エラー auto f() { return 1337 ; } int j = f() ; // OK
明示的特殊化の場合、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
また、同じ型であると推定される複数のreturn文の記述を許そうという提案もしている。
auto negate( bool b ) { if ( b ) return false ; else return true ; }
関数内のすべてのreturn文の型が同じかどうかを判別することは可能なので、これを禁止する理由はない。
ただし、再帰は話が違ってくる。単純に再帰する関数の戻り値の型は決定できないので、当然ill-formedになる。
// エラー auto f() { return f() ; } ;ただし、もし他のreturn文から戻り値の型が推測できれば、それによって再帰が書ける。そのようなコードは許そうではないかと提案している。
auto sum(int i) { if (i == 1) return i; // 戻り値の型はint else return sum(i-1)+i; // OK }
autoに対する宣言子も働く。
auto & f() { static int x ; return x ; }
N3387: Overload resolution tiebreakers for integer types
standard conversionはsignedからunsignedへの極めて強い暗黙の変換を行うので、これがオーバーロード解決の際に問題になる。
たとえば、多長精度の正の整数を表現するクラス、BigUnsignedIntを実装したいとする。手軽に使うために、コンストラクターで整数リテラルを受け取るが、負数を渡してほしくはない。しかし、単にunsignedな整数型を使った場合、standard conversionにより負数がunsigned型に変換されてしまう。
struct BigUnsignedInt { BigUnsginedInt( unsigned long long ) ; } ; int main() { BigUnsignedInt b = -1 ; }
では、signed型のコンストラクターも追加して、オーバーロード解決で振り分けようとしても、曖昧になる。
struct BigUnsignedInt { BigUnsignedInt( unsigned long long ) ; BigUnsignedInt( long long ) = delete ; } ; int main() { BigUnsignedInt b = 5 ; // エラー、曖昧 }
この問題は、standard conversionのためである。この問題を解決するには、すべての整数型をオーバーロードして振り分けるしかない。
残念ながら、この問題を完全に解決するには、既存のコードをほとんど壊してしまうので無理だ。そこで、オーバーロード解決を少し変更することで、この問題だけは修正しようという提案。
Asioの紹介、並びにC++11で実装したらどうなるかという実験的実装。AsioのようなライブラリをC++の標準ライブラリに採用するには、相当な困難が待ち受けているであろうが。
ソースコードはgithubで公開されている。
chriskohlhoff/asio at cpp11-only · GitHub
N3389: Urdl: a simple library for accessing web content
AsioとOpenSSLの上に構築された、URLを使って参照されたWeb上のデータをダウンロードするためのライブラリの紹介。標準ライブラリへの提案というのではなく、Asioを使った上位層のソフトウェアの実装紹介の意味合いが大きい。
ソースコードはSourceforgeで公開されている。
Urdl C++ Library | Free software downloads at SourceForge.net
そうか。ついに来たか。
xkcd: So It Has Come To This
女「キャットフードを切らしてしまったわ」
男「そうか。ついに来たか」笑いどころ解説:何と言うべきか思いつかない場合は、「そうか。ついに来たか」と言っておけばいい。このセリフは一瞬にして緊張感をもたらし、どのようなシチュエーションでも使うことができる。
標準ライブラリにAnyライブラリを提案。
AnyはBoostのライブラリのひとつで、Type Erasureという技法により、どんな型でも格納できるクラスである。その強烈な見た目に反して、実装はとても簡単である。仮想関数を持つ非テンプレートなクラスから派生するクラステンプレートを内部的に使用することで実装できる。
実のところ、Anyというのはとても単純で基本的なライブラリであり、std::functionやstd::bindなどは、Anyの技法の特殊な実装とでも言うべきものである。
N3391: ISO C++ SG1 Meeting Minutes for May 2012
N3392: Minutes, WG21/SG4 Meeting 8 May 2012 Redmond, Washington, USA
5月に行われた会議の様子の報告。C++11にはもっと多くの標準ライブラリが必要なので、ライブラリに関する話になっている。
N3394: [[deprecated]] attribute
属性にdeprecatedキーワードを付け加える。これにより、古くて使用を推奨しないエンティティをマークして、ユーザーが利用した時に警告を発することができるようになる。
すでに、同等の機能は、gccとclangとMSVCとEmbarcaderoで実装されている。文法の違いはあるが、挙動はどれもほぼ同じとなっている。すなわち、
- deprecatedされたエンティティが使用された場合、コンパイラーはdiagnostic messageを出す
- deprecationにはメッセージを付加することができ、その場合、コンパイラーは付加されたメッセージをdiagnostic messageに含める
- このdiagnositic messageのデフォルトの挙動は、「警告」である。
この機能は問題なく規格に取り入れられるだろうと思う。
現時点で、ストリームは競合を起こさないことが保証されている。しかし、その結果については保証しないので、結果を保証させたい場合、自前で排他的制御を設けなければならない。そこで、ストリームにロック機能を付加した、stream_mutexを提案する。
N3396: Dynamic memory allocation for over-aligned data
現在、規格では、動的に確保するストレージのアライメントが正しい保証はない。それに、ユーザー定義のアロケーション関数の場合、アライメントを知るすべがない。これをどうするのか。
この提案では、様々な議論を挙げた結果、アライメント数を受け取るアロケーション関数とデアロケーション関数を新たに追加することにしている。どうなることやら。
文字列などの変換を行うライブラリ。
この提案は、char8_tをunsigned charのtypedef名としている。signed char, unsigned char, charは区別されるので、通常のcharとは区別できるから問題ないとしている。そんな奇妙な解決方法は嫌だ。char8_tは本物の型であるべきだし、そもそもUTF-8文字リテラルとUTF-8文字列リテラルは、char8_t型であるべきだったのだ。
char8_tをunsigned charのtypedef名とする、この提案はクソだ。それならない方がましだ。
Filesystemライブラリの提案の改訂3版。
N3400: A proposal for eliminating the underscore madness that library writers have to suffer
そうか。ついに来たか。しかし計画とはだいぶ異なるな。
ユーザー定義されたマクロ展開による意図せぬ意味の変更を防ぐため、C++11実装のライブラリは、ダブルアンダースコアーを多用している。
template< typename __Iter > void sort( __Iter __first, __Iter __last) ;
このコードは、ユーザーがIterとかfirstとかlastなどというマクロを定義しているかもしれないことを考慮して、すべての名前をダブルアンダースコアから始めている。規格上、ダブルアンダースコアから始まる名前は実装に予約されているので、安全に使えるためだ。
このコードは汚いし、何よりユーザーのライブラリには、同等の機能が提供されていない。
過去に、横暴なマクロ展開を何とかしようという試みは何度も行われてきたが、ことごとく失敗した。理由は、解決策がどれも机上の空論で、現実にはまったく使えなかったからだ。
過去に最も議論されたものとしては、プロプロセッサーにスコープを設けるという方法があった。しかし、既存のコードでマクロは多用されており、一律にマクロを無効化してしまうプリプロセッサーのスコープは、現実的には無価値だった。
今回の提案は、プリプロセッサーに優先度を設けるというものである。この優先度は、実装が定めるもので、規格では定めない。優先度の高いソースコードに対して、優先度の低いソースコードのプリプロセッサーのマクロは展開されない。これにより、C++の実装やシステムの実装は、ユーザーが定義したマクロの展開を恐れる必要はなくなるし、サードパーティライブラリも、ユーザーが定義したマクロの展開を恐れる必要はなくなる。
これは机上の空論ではなく、筆者のJonathanが考案し、実装もした結果であるとのこと。
いつかプリプロセッサーがなくなるその日まで。
ワシはプリプロセッサーなど大嫌いだし、今でも嫌いだ。スコープや型やインターフェースからなるプログラミング言語において、文字とファイルからなるプリプロセッサーは根本的におかしい。
Cプリプロセッサーが打ち棄てられるのをこの目で見たい。
Bjarne Stroustrup、D&E、Cプリプロセッサーについて
N3401: Generating move operations (elaborating on Core 1402)
move constructorがいつdeleted定義されるのかという条件について、色々と問題が多い。変更しても結局問題が多い。
N3402: User-defined Literals for Standard Library Types
STLのstd::basic_stringとかstd::complexやstd::chrono::durationなどにユーザー定義リテラルを提供しようという提案。さらに、二進数のユーザー定義リテラルも提案している。
コンパイル時定数にするために、実装がテンプレートメタプログラミングを多用したコードになってしまう。また、2進数リテラルは、ライブラリではなくコア言語で定義すべきだと思うのだが。8進数10進数16進数はプレフィクスの違いで表現するのに、2進数はライブラリベースの実装でポストフィクスというのは混乱を招くだけだと思うのだが。
コンパイル時atoiの存在は嬉しいにしても、やはり整数リテラルはコア言語で定義したほうがいいと思うのだが。
N3403: Use Cases for Compile-Time Reflection
そうか。ついに来たか。
C++にリフレクションを追加するための議論の題材として、リフレクションはどのように使われるのか。その利用例にはどのような機能が必要かということを、いくつかの例を挙げて紹介している。これが全てではないが、まあ、議論をはじめる材料としてはいい。
もちろん、C++でリフレクションといえば、コンパイル時の静的リフレクションである。
利用例として挙げているのは以下の通り。
- Serialization
- Parallel hierarchies
- Delegates
- Getter/Setter generation
- Generating user interfaces to call functions and constructors
必要な機能としては、
- クラスのメンバーと基本クラスを、privateなものも含めて列挙できる機能
- クラスのメンバーを作り出す機能と、それに伴うコンパイル時文字列
- 関数の仮引数を列挙する機能と、仮引数を参照する機能
その他、最後の"Generating user interfaces to call functions and constructors"に関して、ソースコードのコメントが列挙できればDoxygen風の利用ができるので、便利ではないかとも記述している。
ともかく夢のある機能だ。
tupleに型で要素にアクセスする機能と、関数オブジェクトと実引数のペアのtupleを用意すれば呼び出せる機能の提案。
テンプレートにちょっとした改良を加える提案。ただし、相当に便利な機能である。
非型テンプレート仮引数は、型を指定しなければならない。
struct X { void f() { } } ; struct Y { void f() { } } ; template < void (X::* member )() > struct traits { } ; int main() { using x = traits< &X::f > ; // OK using y = traits< &Y::f > ; // エラー }
どんな型の非型テンプレート実引数でも受け取るテンプレートは、以下のように書ける。
tempalte < typename T, T t > struct traits { } ;
ただし、使うのは面倒だ。
using x = traits< decltype(&X::f), &X::f > ; using y = traits< decltype(&Y::f), &Y::f > ;
この最初のテンプレート仮引数は、コンパイラーが推定できるべきである。そのために、そのような文法を追加する。提案では、以下のように書けばいい。
template < typename T t > struct traits { } ; int main() { using x = traits< &X::f > ; using y = traits< &Y::f > ; }
これで、任意の型の非型テンプレート実引数を取れるようになる。Tは非型テンプレート実引数の型が、tは非型テンプレート実引数を受け取る。
提案では、この機能は非型テンプレート実引数の情報を表示するtraitsが動機であるというが、そのサンプルコードが興味深い。
struct A { void f(int i); double g(size_t s); }; /* ... */ cout << describe<&A::f>::name; // Prints "f" cout << describe<&A::g>::arity; // prints 1
メンバーの情報を返すリフレクションtraitsとなっているのだが、名前を文字列で返すらしい。今のC++11には、そのような機能はない。このようなことを特に言及するでもなくさらりと書いているのだから、C++1yには、もしかすると静的リフレクションが入ってしまうかもしれない。
また、長年議論されている、クラステンプレートのコンストラクターからArgument Deductionできるようにしようという提案もある。
template < typename T > struct X { X( T ) ; } ; int main() { X x( 0 ) ; // X<int> }
いままでなら、make_Xのようなヘルパー関数テンプレートを作る必要があったが、その必要がなくなる。
さらに小さな変更だが、仮引数パックを型と非型で区切れるようにしようという提案もある。たとえば、以下のようなコードが動くようになる。
template < typename ... T, T ... t > struct X { } ; int main() { X< int, double, int, 1, 1.0, 1 > x ; } ;
型と非型で区切り目が明らかに区別できるのだから、これが認められてもいいじゃないかという提案だ。
そして、完全に特殊化されていた場合に限り、メンバー関数テンプレートをvirtualにできるようにしようという提案もある。これは、Modern C++ Designのsection 3.1を受けたものだ。
N3406: A proposal to add a utility class to represent optional objects (Revision 2)
Boostにあるoptionalを標準ライブラリに入れる提案。
N3407: Proposal to Add Decimal Floating Point Support to C++
10進浮動小数点数の提案。ライブラリでの実装になる。
N3408: Parallelizing The Standard Algorithms Library
STLにある既存のアルゴリズムの、並列実行版を追加する提案。thrustを元にしている。従来のSTLのアルゴリズムに親しんだC++プログラマーならば、そのまま使えるインターフェースになっている。
なぜ並列実行アルゴリズムのライブラリを標準化するのかとか、このライブラリを標準化するのは正しいのかとかなどの疑問にも回答している。
現在の環境ごとのスレッドモデルには様々な違いがあり、また制約も多い。特にベンダー独自の仕組みが多く、移植性がない。STLのなじみあるアルゴリズムを並列実行版として提供して、実装は各自に任せるという方法が最適であると結論している。
N3409: Strict Fork-Join Parallelism
N3408とはうってかわって、こちらはstrict Fork-Joinという実行モデルのサポートのために、コア言語による文法としてのサポートが必須であるという主張になっている。
この提案に規格の文面が存在するわけではないが、土台としてはIntel® Cilk™ Plusコンパイラーの文法を改良したものになっている。さらに、タイトルにもあるように、Fork-joinという、多数の実行単位を発生(fork)させて、実行の終了を待つ(join)という実行モデルを採用している。さらにstrictという概念があって、色々とその意味や、この実行モデルを採用する理由について解説している。
N3410: Rich Pointers with Dynamic and Static Introspection
C++に静的動的両方のリフレクション機能のうち、型やポインターの参照先の型情報を詮索する機能の提案。コンパイラーによる支援とライブラリの二本立てになっている。
この機能により、関数型の戻り値の型や仮引数の個数とそれぞれの型、クラスの基本クラスやメンバーなどの情報を、静的、動的の両方で列挙してアクセスできる。
コンパイル時はもとより、実行時の型情報も重要である。C++は極めて単純な実行時型情報の機能を提供しているが、これは十分ではない。もっと詳しい詮索がしたいものだ。Bjarne Stroustrupも書いたように、ある言語にある機能が存在しないならば、プログラマーはその言語上で実装する。実装するコストが割りに合わないのであれば、言語で直接サポートしたほうがよい。
既存の例として、QtやGoogle Protocol Buffersが挙げられている。Qtはプリプロセッサーマクロを多用して 手動で実行時型情報のためのテーブルを構築するものであり、Google Protocol Buffersは、専用のコンパイラーを使って機械的に生成するものである。どちらも、専用のフレームワーク内でしか動かない。
N3411: Additional Searching Algorithms
検索アルゴリズムにBoyer–MooreとBoyer-Moore-Horspoolを追加する提案。Boyer-Mooreは効率のいい文字列検索で有名なアルゴリズムだ。ただし、検索前に検索用テーブルを構築しなければならず、そのテーブルのためにメモリ消費量が多いアルゴリズムなのだが、それなりの量のデータ列から、複数の要素の連続したパターンを探す場合には、テーブル構築によるロスはすぐにかき消される。Boyer-Moore-Horspoolはその変種で、テーブル構築の一部を省略することで、メモリ消費量が少ない代わりに、worst caseで劣るアルゴリズムである。
Boyer-Mooreといえば検索前にテーブルを構築しなければならないのだが、narrow character以外の多数の状態をもつような型に対しては、ハッシュテーブルを用いるらしい。なるほど。
N3412: Runtime-sized arrays with automatic storage duration
すでに2月のmailingで出されたが、配列の長さを実行時に指定できるようにする提案。C99に存在し、またGCCも早くから独自拡張として提供していた機能だ。
使い方は以下の通り。通常の配列の宣言の添字をコンパイル時定数にしなくてもよくなるだけだ。
void f( std::size_t n ) { int a[n] ; }
N3413: Allowing arbitrary literal types for non-type template parameters
そうか。ついに来たか。
非型テンプレート仮引数に任意のリテラル型を許可するぶっ飛んだ提案。
もはや浮動小数点数がどうとかいう話ではない。リテラル型ということは、条件を満たせばクラスでも可能だという事だ。constexprを使えばコンストラクターも持てる。
struct L { constexpr L( int v ) v(v) { } int v ; } ; template < typename T > struct S { } ; S<L(1)> s ;
すると問題になるのは, リテラル型を非型テンプレート実引数に取るテンプレートの型が等しいかどうかを判定する方法だ。型が等しいかどうか判定するには、非型テンプレート実引数が等しくなければならない。クラスである以上operator ==で比較すべきだろうか。問題は、constexprがある以上、リテラル型でもoperator ==は実装できる。
struct L { constexpr L( int v ) : v(v) { } int v ; constexpr bool operator == ( L x, L y ) { return true ; } } ;
すると、S<L(1)>とS<L(2)>は同じ型という事になる。
何が困るかというと、C++は、古典的なリンカーで実装できるように設計されている。C++では、テンプレートの特殊化である実際の型には、シンボル名にテンプレート実引数などの情報を埋め込んでいる。これをname manglingという。リンカーは、単にシンボル名が完全一致するかどうかで、型が同じかどうかを判断できる。
このペーパーでは、operator ==を使うのは実装上難しいので、メンバーごとの比較にしてはどうかと提案している。このあたりはさらなる議論が必要そうだ。実装経験がないのも懸念される。
N3414: A Rational Number Library for C++
タイトル通り、有理数ライブラリの提案。
数学の知識がないのを棚にあげて言うのだが、この手の数学的な簡易なクラスのライブラリが実際の現場で使われることがあるのだろうか。<cmath>にあるような基本的な関数はともかくとして、この手の複素数やら有理数やらの汎用的で簡易な実装が、現実に使えるものなのだろうか。
N3415: A Database Access Library
C++にデータベースにアクセスするためのライブラリを設計するとしたら、そのインターフェースはどのようになるかという可能性を示す提案。もちろん、C++のすべての実装にデータベースライブラリの実装を要求するのはありえないので、これは技術規格への提案ではなく、Technical Reportへの提案の形を取っている。
N3416: Packaging Parameter Packs
そうか、ついに来たか。
純粋なパラメーターパックに名前をつけられるようにする提案。もっと具体的に言うと。型リストをコア言語でサポートするという提案。
従来、型リストはライブラリでサポートされていた。C++で型リストを積極的に使った有名な本は、Modern C++ Designである。ただ、Lokiの実装は古い。最近では、型リストにコンテナーやイテレーターの概念を持ち込んだBoostのMPLがある。もっともこれも古い実装だ。C++11の機能を十分に活用した全く新しい型リストの開発が望まれる。標準ライブラリのtupleは、非常に貧弱である。
なぜ型リストをコア言語でサポートする必要があるのか。「既存の型リストのライブラリの存在と利用から分かるように、型リストは有益である。しかし、現状のライブラリは、言語に制限されている。したがって、型リストの実装を助けるようなコア言語の機能が必要である」というのが、この提案の主張らしい。この提案は、コア言語で型リストを完全に実装するのではなく、ライブラリの実装を助けるコア言語の機能の提案である。
この提案は、パラメーターパックリテラルというものを導入する。パラメーターパックリテラルは、<>で囲まれたテンプレートパラメーターリストである。
< int, short, 1, double >
型と非型を両方とも含めることができる。おそらく、テンプレートも含められるだろう。
パラメーターパックリテラルには名前を付けられる。
typedef<signed char, short int, int, long int, long long int> signed_integral_types; cout << contains<signed_integral_types, int>::value; // Prints true
これにより、テンプレート外からでも、型リストを簡単に使える。
containsというのは仮に考案されたライブラリで、テンプレートパラメーターリストに与えられた一つ目のパラメーターパックリテラルから、二つ目のテンプレート実引数が含まれているかどうかを探すものだろう。
この使用例からも分かるように、パラメーターパックリテラルは、それ自体を区別して、展開されないままに、テンプレート実引数に取ることができる。
// Inherits from true_type if typelist TL includes all the types in TL2 template< <typename...> TL, <typename...> TL2> struct contains;
<typename...> という記法が、非展開のパラメーターパックリストを受け取る文法だ。
また、この提案の副産物として、型や非型を区別せずに受け取るテンプレートを記述できるようになる。...に対して、.を使う珍妙な文法になるのだが。
N3417: Proposal for Unbounded-Precision Integer Types
そうか。ついに来たか。
無限精度の整数型ライブラリの提案。
基本型の整数型の精度では足りないという事がよくある。その時、無限精度の整数ライブラリが標準ライブラリにあればいいのにと、いつも思う。実際、多くのプログラミング言語は、無限精度の整数演算を提供している。C++にもあってしかるべきだ。
このライブラリは、C++に標準ライブラリとして存在しないのが不思議でならない。たしかに、すべての環境で必要なライブラリではないだろうが、標準ライブラリに入っているべきだ。
思うに、このライブラリに対する要求が、人それぞれ甚だ異なるからではないだろうか。ある者は最高のパフォーマンスを求め、またあるものは、正しく動き、手軽に使えるライブラリを求める。
Boostでも、無限精度の整数ライブラリがレビューにかかるたびに、いつも賛否両論のレビューが寄せられ、結果として、より議論を深めてからということで、お流れになる。
理由は、パフォーマンスだとか使い勝手だとか様々だ。思うに、万人を満足させる無限精度整数ライブラリなど不可能だ。現実に、このライブラリは必要とされている。このライブラリは、純粋にプログラミングの問題だから、実装は楽しい。多くのプログラマーは、プログラミング学習の一環として実装したりもする。しかし、だからこそ、自前で実装するべきではないライブラリともいえる。そのようなライブラリは、気軽に自前実装するべきものではなく、十分に検証された実装を使うべきだ。
誰もがその必要性を認めながら、細部で同意が得られないために、なかなか採択されないライブラリというのは、不幸なことだ。
N3418: Proposal for Generic (Polymorphic) Lambda Expressions
ジェネリックlambda式、あるいはポリモーフィックlambda式の提案。
今のlambda式は、ジェネリックではない。例えば以下の関数テンプレートに渡すlambda式を書くことは難しい
template < typename Func > void f( Func func ) { func( 123 ) ; func( 0.001 ) ; func( "hello" ) ; }
これは、現状のlambda式がジェネリックではないためである。lambda式の引数には、具体的な型を指定しなければならないのだ。もちろん、Boost.Anyのようなクラスを引数に取るlambda式なら書けるが、ここではそのような方法は使いたくない。
しかし、lambda式を使わずに、従来のクラスで実装した関数オブジェクトを使えば、この関数テンプレートに渡す関数オブジェクトは、簡単に実装できる。
class FuncObj { public : template < typename T > void operator ()( T t ) const ; } ; int main() { FuncObj funcobj ; f( funcobj ) ; }
単なるシンタックスシュガーであるlambda式に、これができないのは理不尽だ。以下のようにかけたら何とすばらしいことか。
f( []( x ) { } ) ;
つまり、lambda式の引数の型を省略して、テンプレートのように振舞わせるのだ。
ジェネリックlambda式は、C++11にlambda式を採用する際にも考慮されたのだが、コンセプトと併用した場合の見解決の問題があったために、却下された。コンセプトは最終的にC++11に採用されなかったが、ジェネリックlambda式の復活はなかった。検証する時間も足りなかったので、妥当な判断だったと思うが、次の規格改訂には是非とも採用して欲しい機能だ。
lambda式に使い慣れたテンプレート仮引数リストの文法を記述できるようにし、複数のlambda式を渡して優先順位を付けられるようにする機能も提案されている。
また、関数の本体を式にすることも提案されている。たとえば、
[]( x ) { return x + 1 ; } ;
というlambda式は、
[]( x ) x + 1 ;
と書けるようになる。
また、この文法を利用して、通常の関数テンプレートを、lambda式の形で書くことができるようにする提案もされている。つまり、
template < typename T > T const & min( T const & x, T const & y ) { return x < y ? x : y ; }
と書いていたのが、
[]min( const & x, const & y ) x < y ? x : y ;
と書けるようになる。私は冗長な文法でも特に構わないと思うのだが、lambdaを好む人間は、冗長な文法を嫌う傾向にあるので、このほうが好ましく感じるのだろう。分からないでもない。私はむしろ、D言語のように、関数の宣言には、functionのようなキーワードを使うほうが、わかりやすくていいと思うのだが。
N3419: Vector loops and Parallel Loops
新しいループの文法として、ベクトルループとパラレルループを追加する提案。パラレルループは、ループの本体を順不同で同時並行に実行するものである。パラレルループはループ終了の条件に厳しい制限があり、またループ本体でも、returnやbreakやgotoなどといったループ内外に制御を移すことができないといった制限がある。ベクトルループは、ループの実行前にループ回数が分かっているカウンタブルループに対してのみ使うことができるループである。
どちらのループも、制限が多く、すべてのループに適用できるわけではない。しかし、もはや純粋にCPUのパフォーマンスが向上する時代が終わり、並列実行を本格的に考えなければならない時代、やはりこのような機能も考えていかなければならないのだろう。
URIを扱うライブラリの提案。URIをパースして妥当性を検証したり、各コンポーネントに分割したり、また各コンポーネントを結合したり、パーセントエンコードやノーマライゼーションなどといった、各種URIに関わる処理ができるライブラリ。
N3421: Making Operator Functors greater<>
<functional>のgreaterなどの比較用のクラステンプレートを、テンプレート実引数なしで使えるようにするもの。
思うに、そもそもクラステンプレートとして実装する必要はなかったと思うのだが。むしろ別の名前空間に同名のクラスでメンバーテンプレートを持っているものを作ったほうがいいのではないか。
N3424: Lambda Correctness and Usability Issues
Lambda式の正しい使用が、時として困惑をもたらすことについて。
ほとんどのプログラマーは、プログラミング言語を、規格書や文法定義から学んだりしない。たいていは、参考に書かれたサンプルコードなどをみて、思い込みのメンタルモデルを構築するものだ。それは悪くない。もし、ある言語が、その見た目から想定外の挙動をするならば、それは言語の設計が悪いのだ。
lambda式が採用され、実装され、使用経験が積もってきた所で、ユーザービリティの問題が浮上してきた。
lambda式を多用している巨大なコードベースを持つある顧客が、lambda式に起因するバグを調べたところ、ほとんどのバグはある一つの言語の誤解から生じているが判明した。暗黙のthisのキャプチャーである。
ちなみにこのペーパーの著者はMicrosoftのHerb Sutterなので、顧客というのは、Visual Studioの利用者だろう。
lambda式のキャプチャーにおいて、thisはやや特別な扱いを受ける。thisというのは、ポインターである。そのため、キャプチャーするのもポインターだ。lambda式の中における、クラスのデータメンバーは、thisポインターを経由して使う。ということは、データメンバーを値でキャプチャーするということはできない。
struct Foo { int z ; void f( int x ) { int y ; auto l = [=] { // すべての変数を暗黙的に値でキャプチャー x ; // 値でキャプチャー y ; // 値でキャプチャー z ; // キャプチャーしたthis->z } ; } } ;
Herb Sutterが関わったプログラマーは全員、ここでzがthisポインターを経由して使われることを期待してはいなかった。キャプチャーリストで、すべての変数を暗黙的に値でキャプチャーするように指定したのだから、zは値でキャプチャーされるべきだと考えていた。実際には、thisを経由して使われるので、実質的には、リファレンスである。
さらに、多くのプログラマーが、データメンバーといえども、個々に値でキャプチャーしたいと考えていることが判明した。現状のC++11では、データメンバーを値でキャプチャーする方法はない。どうしても値でキャプチャーしたければ、同名のローカル変数でデータメンバーの名前を隠すしかない。
事実として、この仕様はすべてのプログラマーに誤解を引き起こしている。しかし、修正するとなると、互換性を壊すような修正になる。しかし、すべてのプログラマーが誤解する機能であれば、修正したほうがいいのではないか。
提案されている中でもっとも良い修正案では、データメンバーも個々にキャプチャーできるようにし、データメンバーを参照したい場合は、this->zのように、明示的にthisポインターを使わなければならなくするように提案している。
もうひとつの誤解は、値でキャプチャーした変数は、デフォルトでconst修飾されているという事だ。
void f() { int x = 0 ; [=]{ x += 1 ; } ; // エラー、mutableが必要 }
コピーキャプチャーされた変数を書き換えるには、明示的にmutableキーワードを使わなければならない。
[=]() mutable { x += 1 ; }
この機能は、安全のために取り入れられた。というのも、コピーキャプチャーされた変数は、クロージャーオブジェクトのデータメンバーであるので、クロージャーオブジェクトのoperator ()を呼び出すたびに、変更されてしまう。しかし、すべてのプログラマーは、この挙動に不審を覚える。余計なお世話ではないか。すでに明示的にコピーすると宣言しているのに、なんでデフォルトで変更できないようになっているのだ。そもそも、C++は、コピーした値がデフォルトでは変更できないようにはしていない。
void f( int x ) // 変数は値渡しされるという意味は明らか { x = 0 ; // OK、コピーされた値の変更は許可されている }
このコードが全プログラマーが同意する挙動である。もし、このコードが、mutableをつけない限り拒絶されるとしたら、それこそ「余計なお世話」である。
そこで、lambda式のmutableを廃止しようという修正を提案している。
N3425: Concurrent Unordered Associative Containers for C++
ロックフリーでデータ競合を引き起こさない要素の追加や検索を実現するunorderedコンテナーを追加する提案。
unorderedコンテナーをロックフリーに実装する方法が確立されている。提案されているコンテナーは、eraseやbucket操作などを除けば、安全に使用できる。
提案では、eraseなどの操作は、ロックフリーにしないほうがいいとしている。そのため、unsafe_eraseなどのような名前をつけることを提案している。ただし、clearやswapのような有名な名前は、名前自体が有名であるし、その要素を取り除く意味が明らかであるので、そのままにするべきだとも提案している、ロックフリーに実装することもできるが、パフォーマンスに与えるコストが大きいため、実装するとしても、デフォルトでは無効にしておき、テンプレート仮引数で有効を切り替えるような実装にするべきだと提案している。
N3426: Experience with Pre-Parsed Headers
Googleが社内でGCCを改良して、ヘッダーファイルのビルドのパフォーマンスを向上させようとした経験からの報告。なかなか興味深い。この経験は、プリプロセッサーによるテキストの取り込みの代わりになる、モジュールの機能を議論する際に参考になるだろう。
GCCも含めて、多くのC++コンパイラーは、コンパイル済みヘッダーという機能を提供している。これは、すべてのソースファイルからincludeされるほとんど変更しない共通のヘッダー群をあらかじめコンパイルしておいて、その結果を読み込むことで、ヘッダーのコンパイルを省略し、もってパフォーマンスの向上を図る機能である。問題は、Googleのコードベースには、そのような、すべてのソースファイルからinlucdeされるほとんど変更しない共通のヘッダー群など存在しない。
そこでGoogleが試したのは、パース済みヘッダーだ。パースを行った結果のGCCの内部表現を吐き出し、あとで必要になった時に読み込むというものだ。これにより、単一の巨大なプリコンパイル済みヘッダーファイルに依存することがなくなり、あらゆるヘッダーファイルが変更される環境でも、ヘッダーファイルに対応するパース済みヘッダーファイルのみが影響を受けるだけになる。
問題は、ヘッダーファイルの意味は、その前にincludeしたヘッダーファイルのマクロ定義によって変わってしまうということだ。したがって、モジュラーなヘッダーを、以下のように定義する。
- インクルードガードを使っている
- 単離した環境でプリプロセスしてパースできる
- グローバル名前空間にincludeされる
調査の結果、ほとんどのアプリケーションのヘッダーは、モジュラーであった。問題は、システムヘッダーの多くが、非モジュラーなヘッダーだったのだ。
ある社内のプロジェクトに結果を適用してみた結果、パースにかかる時間が半分になり、結果として全体のビルド時間が5倍になった。
しかし問題はある。パース済みのヘッダーは、依然としてヘッダーであることにはかわりがないので、プリプロセスは依然として行わなければならない。そのため、プリプロセッサーマクロは保持して、パース済みヘッダーを読み込む際に再生しなければならない。
同じパース済みヘッダーが二度includeされる状況に対応するために、同一のシンボルはマージしなければならない。
GCCは、従来のヘッダー処理のために、多くの箇所で最適化の仕組みがある。この最適化は、パース済みヘッダーのためには、むしろ邪魔である。そのため、いちいち潰したり、問題にならないように対処したりする必要がある。
さらに、GCCのパース済みヘッダーの内部表現は、一度書きだして後で読み込むといった利用方法のことは考慮されていないので、コード生成に必要なヒントなどの情報を含み、無駄が多い。
もっと問題になるのは、Googleのビルドシステムだ。Google社内のビルドシステムは、極端に並列化されている。ただし、マルチコアシステムが一般的になった今、ビルドシステムが並列化されているのは、Googleだけの特殊な環境ではないと言える。
ビルドの過程には、並列化できない依存関係がある。並行ビルドを行う前に、そのような依存関係をすべて洗い出してビルドに必要な依存グラフを構築しなければならない。ところが、パース済みヘッダーファイルを生成するということは、新たな依存関係を持ち込むという事である。このために、依存関係が複雑になり、巨大なコードベースにおける依存グラフの構築にも時間がかかるようになった。
さらに、シンボルのマージは、実装が難しく多くのバグを誘発した。処理にも時間がかかる。
もっと問題なのは、問題の種類が変わってきたことだ。
GCC本体の最適化により、プリプロセスやパースにかかる時間が減ってきた。さらに、ビルドプロセスには、コンパイル以外の他のツールも関わるようになった。例えばソースコードの静的解析ツールなどだ。googleのビルドシステムでは、clangでもビルドして、静的に発見できるコード上のエラーを見つける試みもしている。
結果として、ビルド時間全体の80%を占めていたパースは、今や全体の60%の時間しかかからず、パースのパフォーマンスを倍にしたとしても、結果は5倍ではなく2.5倍の向上に留まってしまった。
よほどマージの実装に苦労したらしく、さらにマージの問題点やら、バグに悩まされたことやら、マージをしなくてもよければパフォーマンス向上はすばらしいなどと続く。
パース済みヘッダーの経験からのまとめ。
C++ヘッダーモデルは生産性の妨げになっている。
従来、下位互換性といえば、既存のコードをそのまま同じようにコンパイルできることを意味していたが、今はソースコードの自動変換技術も発達したので、自動変換できるかどうかで判断すべきであること。
モジュール機能の設計にあたっては、ビルドシステムのことを無視してはいけないこと。
必要なだけのシンボルを外部にエクスポートできる機能があれば、パフォーマンスの向上につながること。
シンボルのマージは非常に高くつき、また実装の難しい処理であること。
あるひとつのシンボルはあるひとつのモジュールだけで提供されていることは重要であること。モジュール機能は、シンボルの再定義を禁止すれば、パフォーマンス向上につながるのではないか云々。
現状で、アプリケーションヘッダーは十分にモジュラーであるが、システムヘッダーはモジュラーではない。モジュール機能のためには、従来のシステムヘッダーを書きなおす必要がある。
ああ、Cプリプロセッサーが廃止される日をこの目で見たい。
読み手は多いが書き手は少ない状況に向けた、やや効率的なロック機構を実装できる。mutlple-readers single-writer locking patternをサポートする、shared_mutexライブラリの提案。
N3428L A Standard Programmatic Interface for Asynchronous Operations
入出力の非同期処理は非常に重要になってきている。std::futureを改良して、非同期処理にも十分に対応できるようにしようという提案。
N3429: A C++ Library Solution To Parallelism
ライブラリで並列実行をする際に、どのような機能を提供できるかという考察。
文字列をデリミターによって分割する処理、いわゆるsplitは、汎用プログラミング言語には極めて一般的なものである。ところが、C++にはそのようなライブラリがない。そのため、各自で実装しなければならない。
たとえば、以下のような関数になるだろう。
std::vector<std::string> my_split(const std::string& text, const std::string& delimiter);
しかし、別のコンテナーで返したいだとか、デリミターを正規表現にしたいだとかいった些細な違いによって、文字列の分割処理は同じだが異なる実装を創りださなければならない。
Google社内では、現実のコードで、50ものそれぞれ微妙に異なるsplitの実装が使われている。
そこで、Google社内で汎用的なsplitを設計して実装したところ、評判が良かったので、標準ライブラリとして提案するそうだ。
iostreamでは空白文字がデリミターとして扱われ、入力と出力が一致しない。そこで、空白文字も含めて引用符的な動作をするマニピュレーターを追加しようという提案。
私が思うに、iostreamはテキスト処理をするべきではなかった。純粋に入出力のみを扱うべきだった。今のiostreamは使いたくないのでどうでもいい。
グローバルなoperator deleteのオーバーロードは、解放すべきストレージのサイズを得ることができない。一方、メンバー関数によるoperator deleteでは、受け取ることができる。モダンなメモリアロケーターはサイズごとにカテゴリ分けされた実装を使う。ストレージの近くにサイズを記録しておくことはない。そのため、現行の実装では、アドレスのみが渡されるので、どこから確保されたのか、検索しなければならない。これはパフォーマンス上コストがかかる。そもそも、コンパイラーはストレージのサイズが分かっているのだから、operator deleteに渡すべきだという提案。
すでにGoogleによってGCCとTCMallocで実装されており、実装経験があるという。
N3433: Clarifying Memory Allocation
動的ストレージの確保はパフォーマンスに直結する問題である。ストレージの確保戦略は、プログラムの動作にあわせて、動的に変更できるべきである。しかし、規格の文面では、ある種の最適化を許していないように解釈できる。
現行の規格の文面を厳格に解釈すると、CとC++の規格は、ストレージ確保は、前後のストレージ確保要求に影響されずに行われなければならないとある。つまり、プログラムのストレージ確保の仕方にあわせて、動的にストレージ確保の戦略を変えるような最適化は、規格上許されない。
たとえば、あるプログラムが大きさや頻度などに特徴のあるストレージ確保を連続して発行した場合に、しばらく同じような特徴を持つ確保が続くだろうと予測して、そのような場合にストレージ確保を最適化するような確保関数の実装は許されない。
現行の規格の文面を厳格に解釈すると、実装はそれぞれの関数呼び出しに対し、それぞれ個別に外部関数を愚直に呼び出さなければならないとある。つまり、複数のストレージ確保をまとめてひとつのストレージ確保として発行するような最適化は、規格上許されない。
たとえば、複数回ストレージ確保を行うコードがあるとして、実装がそれをまとめてひとつのストレージ確保として発行して、ひとつの連続したストレージを得て、分割して使うような最適化は、規格上許されない。
このような最適化を許すように、制限を緩和する。
すなわち、確保関数の呼び出し回数はプログラムから観測可能な挙動ではない。確保関数の仮引数と戻り値は、プログラムから観測可能な挙動ではない(ただし、確保するストレージは、アライメントなどの調整以上に、指定されたサイズを超えてはならない)
つまり、複数回のストレージ確保が、実装により、一度のストレージ確保にまとめられる可能性があるという事だ。つまり、今までならば、
int main() { new int ; new int ; }
と書いた場合、::operator newは二回呼ばれることが保証されていたが、この提案が受け入れられたならば、実装により、一回にまとめられる可能性もあるという事だ。
concurrent queueライブラリの提案。
N3435: Standardized feature-test macros
機能テスト用のマクロの提案。
そもそも、Cプリプロセッサーは一刻も早く滅びるべきであるのに、今さらこのようなマクロを導入するのは気にいらない。Cプリプロセッサーの存在に頼る新機能を導入することは、Cプリプロセッサーの廃止の妨げになる。
かわりに、強力なstatic ifを導入すべきだと思う。コンパイル時に選択されない分岐は、たとえ文法違反でも無視するような仕様であれば、マクロに頼る必要はない。
N3436: std::result_of and SFINAE
std::result_ofは、関数のシグネチャの中で使った場合、SFINAEが発動しない。decltypeを直接使うと、SFINAEが正しく発動する。
ペーパーで使われているサンプルコードはあまりわかりやすくない。そこで、decltypeを使うとコンパイルが通るが、result_ofを使うとエラーになるコードを書いてみた。
struct X { // SFINAEにより、Tがクラス型の場合は選択されない template < typename T, typename Dummy = typename std::enable_if< !std::is_class<T>::value >::type > void operator () ( T ) ; template < typename T > void operator () ( T ) ; } ; // OK、decltypeを直接使うとSFINAEが発動する template < typename T > decltype( std::declval<X>()( std::declval<T>() ) ) f() { } // #1 template < typename T > void f() { } // #2 // エラー、result_ofにSFINAEは発動しない template < typename T > typename std::result_of< X( T ) >::type g() { } template < typename T > void g() { } int main() { f<int>() ; // OK、#2が選ばれる g<int>() ; // エラー、曖昧 }
この問題を解決し、SFINAEが発動するように、result_ofのネストされた型であるtypeは、decltypeを使った式がwell-formedの場合のみ存在するように変更する提案。
N3437: Type Name Strings For C++
C++にリフレクションを入れるにあたって提出されている提案に欠けている重要な事項に、型名を文字列で表現するということがある。現在、typeinfo::name()は、実装依存の人間に読める型を表現する文字列を返すと定義されている。実装依存であるので、この文字列を元にした移植性のあるコードは書けない。
C++にリフレクションを入れる際には、型名を文字列で表現する方法について規格化するのがいかに重要か。どのように利用できるかということを紹介するペーパー。
利用例では、SerializationとType introspectionとLanguage bindingが挙げられている。
N3441: Call Stack Utilities and std::exception Extension Proposal
コールスタック情報の取得と解析の標準ライブラリの提案。さらに、std::exceptionを拡張して、例外が投げられた場所のコールスタック情報を持つようにする提案。
ソフトウェアをデバッグする上で、コールスタック情報を人間が読める形に整形して検証できることは重要である。プログラム内から、コールスタックを取得する標準的な方法が存在するべきである。さらに、例外は、投げられた場所のコールスタックが重要であるから、標準の例外クラスであるstd::exceptionを変更して、コールスタック情報を持つようにする。
提案しているライブラリの、GNU/Linux上で動く実装も公開されている。
http://freeshell.de/~amelinte/lpt.tgz
N3442: string_ref: a non-owning reference to a string
文字列への参照は重要である。多くの場合、呼び出された関数側では、文字列がどのように表現されているかということは重要ではない。生の配列にせよ、クラスでラップされているにせよ、重要なのは、文字列だ。従来、そのような場合に文字列を受け取る場合には、以下の三種類の方法が使われてきた。
- std::stringを使い、文字列が所有されている場合は、コピーを要求する。
- char *と長さを受け取る。あるいはNULL終端であるとみなす。
- 関数の呼び出された実装をテンプレート化し、実装をヘッダーファイルに移す。柔軟ではあるが、弱いイテレーターコンセプトを使わなければならず、コンパイル時間やコードサイズの増加を引き起こし、バグの温床である。
GoogleとLLVMはどちらも、独立して、このような実引数の差異を隠匿する文字列を参照するライブラリを実装している。そのような文字列を参照するライブラリ、string_refを提案する。
N3443: Priority Queue Changes and Additions
現行のpriority_queueを改良する提案。ヒープに対するイテレーター、要素の変更、ヒープの効率的なマージ、安定したソートを選択可、ヒープの同一性の比較の機能を追加する。
N3444: Relaxing syntactic constraints on constexpr functions
そうか、ついに来たか。
constexpr関数の制限を大幅に緩和する提案。
constexpr関数でも、普通に複数の文を書けるようになる。
ただし、いくつかの制限はつく。ローカル変数はリテラル型のみで必ず初期化されなければならず変更することもできないし、ループ構文も使えないし(ループがしたければ再帰)、switch文とgoto文は禁止され、関数が定数式を返すパスが存在しなければならない。
とはいえ、constexpr関数といえども、普通に書けるようになる。
筆者のRichard Smithによるclangでの実験的な実装がgithubで公開されている。
zygoloid/clang at liberal-constexpr · GitHub
Pass by Const Reference or Value
プログラマーは、コストのかかるコピーを避けるためconstなlvalueリファレンスを多用しがちである。リファレンスはポインターのような文法上の汚さがない代わりに、ポインターと同じ問題がある。エイリアシングである。
extern type ra1(const type& input); extern type ra2(const type& input); void rf1(type& output, const type& input) { output += ra1(input); output += ra2(input); }
このようなコードがある場合、もし、関数を呼び出すユーザーが、outputとinputに同じオブジェクトを渡した場合、問題が起きる。
エイリアシングに対処するためには、inputを常にコピーして使うとか、outputとinputのアドレスを比較してエイリアシングを検出するなどの方法がある。しかし、コピーを防ぐためにリファレンスにしたのに、常にコピーしては本末転倒であるし、アドレスを比較するということは、条件分岐をもたらし、深いパイプラインが当たり前になっている今日のCPUアーキテクチャ上では、コピーより高くつく場合もある。
Cのrestrictのような機能は、エイリアシングが起きたら未定義と言うに等しく、C++に導入すべき機能ではない。
ともかく、エイリアシングの問題を考慮しないプログラマーがconstリファレンスをパフォーマンスのために盲目的に使うのが問題であって、効率的に引数として渡す文法を導入してはどうか。つまり、コンパイラーが自動的に、コピーのほうが効率がいい場合はコピーし、リファレンスのほうが効率がいい場合はリファレンスに決定するように支持する文法があればいい。
ペーパーでとりあえず提案されている文法は、以下のようなものである。
extern type oa1(const type| input); extern type oa2(const type| input); void of1(type& output, const type| input) { output += oa1(input); output += oa2(input); }
なんだかよりわかりにくくなったと思うのは、この文法に慣れていないためであろうか。まあ、この文法はあくまで提案であるのだが。
またもやGoogleによる、Mapreduceライブラリの提案。Googleならお得意だとは思うが、それにしても、本当にGoogle由来の提案が多い。今が盛りという事か。10年後はどんな企業に取って代わるのだろうか。
N3448: Painless Digit Separation
数値のリテラルに区切り文字の使用を許す提案は、以前から議論されてきた。
int x = 123_456 ;
あまりに長い数値は、読みやすくするため、途中で区切りたいものだ。
問題は、区切り文字がアンダースコアであると、ユーザー定義リテラルと曖昧になるという事だ。そこで、区切り文字をシングルクオートにする提案。
int x = 123'456 ;
N3449: Open and Efficient Type Switch for C++
ものすごく本格的な論文らしい論文。もちろん、ここで紹介したすべての文章は"paper"であり、日本語に訳せば論文となるのであろうし、一応、すべて論文の体裁は取っているのだが、ここまで本格的な体裁のC++WGの論文は、なかなか珍しい。
内容は、実行時の型による効率的なswitchを実現するライブラリである。標準規格への提案ではなく、技法の紹介のようだ。
Bjarne Stroustrupも共著に名前を載せている。まあ、これは当然のことで、氏はこの手の動的な型による条件分岐が好きなのだ。本音としては、マルチメソッドがC++に採用されて欲しいのだろうが。
Bjarne Stroustrupが共著に載っているためなのかどうかはわからないが、ソースコードにプロポーショナルフォントを用いている。氏はソースコードにプロポーショナルフォントの利用をためらわないことは有名だとはいえ、非常に読みにくい。
ライブラリの実装は以下で公開されている。
Supporting material for OOPSLA-2012 publication
個人的な意見では、このライブラリはクソである。私の基準では、Cプリプロセッサーのマクロの使用が必須のライブラリは例外なくクソだ。このライブラリは、switch文のような文法をエミュレートしようと、Cプリプロセッサーのマクロを多用している。
N3450: Iterator-Related Improvements to Containers
コンテナーにイテレーター関連の極めて些細な改良をいくつか加える提案。この提案されている改良は、現状でも問題なく行えるが、標準で備わっていることにより、教育しやすく、ユーザーコードを短くする。
last
コンテナーは、最初の要素へのイテレーターと、最後の要素の一つ先の終端としてのイテレーターを返す。しかし、時として、最後の要素を指す普通のイテレーターが欲しい場合がある(reverse iteratorではない)。現状では、end()の返すイテレーターから1を減じるしかない。最後の要素へのイテレーターを返すメンバー関数、last()をコンテナーに付け加えてはどうか。追加対象は、forward_listを除く、basic_stringとすべてのコンテナー。
イテレーターとインデックスの相互変換
イテレーターとインデックス(何番目の要素であるか)を相互変換したい場合がよくある。問題は、現状でその変換を正しく行うのは、非常にややこしい。しかも、size_typeとdistance_typeという、二つのネストされた型名を使わなければならない。コンテナーに変換用のメンバー関数、to_iteratorとto_indexを追加してはどうか。追加対象は、basic_string、array、deque、vector。
ペーパーにはdistance_typeと書いているが、そのようなtypedef名はなく、differene_typeの誤りであろう。
Nullイテレーター
コンテナーのオブジェクトに関連付けられていない、何も指していない状態のイテレーターが欲しい。そのため、Nullイテレーターという概念を導入してはどうか。
Mapped typeイテレーター
mapを使っていると、キー要素にマップされた要素だけをイテレートしたいことがよくある。しかし、そのイテレーターはキー要素とマップされた要素のpairなので、わざわざ->secondと書かなければならず、非常にコードが汚くなる。
そこで、mapのアダプターであるmapped_adaptor_tを追加してはどうか。このアダプターを経由してイテレーターを得ると、マップされた要素を返すイテレーターが得られる。
futureとshared_futureのデストラクターはブロックしない。ただし、futureに関連付けられているasyncはブロックする。すると、futureのデストラクターも結果としてブロックする。この要求を廃止し、futureのデストラクターはブロックしないようにする提案。
No comments:
Post a Comment