2014-07 post Rapperswil mailingのレビュー: N4052-N4059
>2014-07 post Rapperswil mailingをレビューしていく。次の論文集の発表までに終わらせたいものだ。
N4052: WG21 2014-06-06 Telecon Minutes
2014年6月6日に行われた電話会議の議事録。
N4053: WG21 2014-06 Rapperswil Minutes
[PDF?] N4054: PL22.16 2014-06 Rapperswil Minutes
2014年6月の16日から21日にかけて行われたRapperswil会議の議事録。二つの議事録の内容はほぼ同一のようだ。
N4055: Ruminations on (node-based) containers and noexcept
vectorを、ムーブコンストラクターがnoexceptな要素と、そうでない要素でパフォーマンスを比較したところ、極端な違いがある。これは、vectorは強い例外保証のために、内部のストレージのサイズを拡大するときに、要素の移動にstd::move_if_noexceptを使ってムーブを試み、noexceptではないムーブコンストラクターを持つ要素の場合は、コピーにフォールバックするからである。
これは、std::vector< std::list<int> > のようなネストされたコンテナーの場合、より極端になる。
なぜlistのようなノードベースのコンテナーのムーブはnoexceptではないのか。listはその設計上、要素の最後に番兵が必要だ。なぜならば、end()はデクリメントできなければならず、デクリメントできるだけの何かを指していなければならないからだ。
さて、この番兵用のノードをどのように確保するかという問題がある。動的に確保するのは普通の方法だ。しかし、どうせ一つしか必要なく、またlistクラスのオブジェクトの生存期間中、常に必要になるので、クラスのオブジェクトのデータメンバーに含めてしまうというのはどうか。これにより、番兵用のノードを動的確保する必要がなくなる。
しかしその場合、listをムーブやswapした時に、番兵用のノードまでムーブやswapできなくなってしまう。つまり、番兵をオブジェクトに含める自走では、listのend()が絡むイテレーターのペアが、ムーブやswap時に無効化されてしまう。listの素晴らしい点は、イテレーターが無効化しないという強い保証にあるというのに。
実は、当初から、listをswapすると、end()イテレーターが無効になるかもしれないと、規格上は定義されていた。そのため、swapした結果、end()イテレーターが無効になっても、規格準拠の標準ライブラリの実装である。問題は、既存の主要なSTLの実装に、swapでend()が無効化しない実装があり、利用者はその挙動に依存してしまっている。それに、先程もいったように、いくら原稿規格が許しているからといって、listでイテレーターが無効になるというのは、listの強いイテレーター保証に対して違和感がある。
結局これは、ムーブ後の標準ライブラリのオブジェクトの状態は、そのオブジェクトは規定されているように使い続けられるという保証があるためにおこる。vectorのストレージ拡大の場合は、そもそもムーブ後のオブジェクトは即座に破棄するので、そもそも必要ない。
論文では、いくつかの改善案を示しているが、どれも根本的に問題を解決するものではないように思われる。
[PDF注意] N4034の破壊的ムーブも、この問題を解決するために提案されたのであろう。
N4056: Minimal incomplete type support for standard containers
STLのコンテナーを再帰的なデータ構造で使えるようにする提案。
// N4056提案
struct X
{
std::vector<X> vec ;
} ;
のようなコードが書けるようになる。具体的には、コンテナーの要素型として、不完全型を認める制限緩和の提案になる。クラスは定義終了である}をもって初めて定義されたとみなされるので、クラス定義の中では、クラス名は不完全型なのだ。
既存の主要なSTLの実装は、かなりのコンテナーで要素型に不完全型を認めている。不完全型でも問題がないかどうかは、実装次第なのだ。Clangのlibc++の実装は、他の実装ならば認めているコンテナーでも、不完全型を認めていないコンテナーがある。
この論文の初版では、すべてのコンテナーに不完全型を認めようという提案であったが、Rapperswil会議で、ひとまず手始めにvectorとlistとforward_listに対しては不完全型を認める制限緩和をしようというコンセンサスが得られたらしいので、そのような提案になっている。
libc++の実装は、この提案を受けて変更が入っている。
[PDFを伝播する必要はない] N4057: A Proposal to Add a Const-Propagating Wrapper to the Standard Library
constメンバー関数は、ポインター風データメンバーの非constメンバー関数を呼び出すことができてしまう。
struct A
{
void bar() const { } // #1
void bar() { } // #2
} ;
struct B
{
B() : ptr( std::make_unique<A>() ) { }
void foo() const { } // #3
void foo() { } // #4
std::unique_ptr<A> ptr ;
} ;
int main()
{
B b ;
b.foo() ; // #4, #2
B const cb ;
cb.foo() ; // #3, #2
}
つまり、メンバー関数のconst性は、ポインターやポインター風のデータメンバーに伝播しない。
これは、C++の文法上、当然のことなのだが、論理的にはひっかかるところもある。そのために、論文では、const性を伝播させるライブラリ、propagate_constを提案している。
struct B
{
// ...
std::propagate_const< std::unique_ptr<A> > ptr ;
} ;
このようにpropagate_constライブラリを使うことで、メンバー関数のconst性を正しく伝播させることができる。
[PDFを使うのは賢くない] N4058: Atomic Smart Pointers
アトミックに操作できる、atomic<shared_ptr<T>>, atomic<weak_ptr<T>>, atomic<unique_ptr<T>>の提案。
これらのスマートポインターに対して、特別にアトミックな特殊化の実装が使われる。
C++では、もはや生のポインターを扱うのはバカのやることである。unique_ptrは生のポインターを扱うのと同等のパフォーマンスと安全性を提供してくれるし、unique_ptrが対応できないような状況にも、shared_ptrは対応できる。しかし、現在のC++で、依然として生のポインターを扱わなければならない分野が存在する。アトミック操作を使ってロックフリーな処理を実装する場合である。このため、atomicに既存のスマートポインター用の特殊化を追加して、アトミックに操作できるようにする提案。
すでに、shared_ptrにはアトミックに操作する関数があるが、クラス外の関数で非常に使いづらい。
なかなかいい提案ではないかと思う。
N4059: Spring 2015 C++ Standards Committee Meeting
2015年4月の4日か9日にかけてに開かれるC++国際会議の案内パンフレット。
Perceptive Softwareがホスト、場所はミズーリ州カンザスシティ。
ドワンゴ広告
先週、土日と有給と、ドワンゴ独自のリフレッシュ休暇を組み合わせて9連休を作り出して、京都に遊びに行った。
休暇中に、なんとなしに吉田寮に転がっていたHaskell入門書を読んでみると、C++にここ数年提案されている新機能は、かなりHaskellの影響を受けているようであった。そろそろ理論を現実に落としこむ時期に来ているのだろう。
ちなみに、あるいかにも研究者らしい生物博士と雑談したところ、次はCommon Lispの番だという。いずれはLispのマクロを導入しなければならず、当然我々はLispをやるべきなのだという。
Cプリプロセッサーのような単なるトークン列によらない、文法やスコープを尊重するマクロは、入るとするならば、おそらく静的リフレクション機能の一つとして入るだろう。コード情報をコンパイル時に扱うことができ、またコンパイル時にコード生成を行えるような機能だ。
完全に遊ぶつもりの休暇であったが、色々と面白いことがあった。その次第は近日中に書く。
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0