本の虫

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

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

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

2014-10-pre-Urbanaのレビュー: N4150-N4159

[論文にPDFを使わない議論が必要] N4150: Alias-Set Attributes: Toward restrict-like aliasing semantics for C++

C99のrestrict風の機能をC++のattributeの文法で提供する提案。

restrictとは、エイリアシングの可能性がないことを明示的にコンパイラーに伝える機能である。これにより、コンパイラーはよりよいコードが生成できるようになる。

C99で追加されたrestrict機能は、すべての主要なC++コンパイラーで、そのままC++にも独自拡張として持ち込まれている。

C言語のrestrictには問題も多い上に、C++に適用するには更に問題も多い。クラスのメンバーやlambda式やテンプレートに対してどう振る舞うのか。restrictは型なのか? ではオーバーロード解決で区別されるのか? 名前マングリングに埋め込まれるのか? C99ですら曖昧な定義をされている機能をそのままC++に持ち込むと問題が多い。

C99のrestrictがすべての主要なC++コンパイラーでサポートされているということは、需要があるということである。さらに現実のプログラマーの意見を聞けば、「よりよいコードを吐くためならばエイリアシングの可能性の有無を明示的に指定する労をいとわない」曰く、「C++11の新機能は使いたいが、ただしパフォーマンスを落とさない場合に限る」

C++の標準にrestrict風の機能がない場合、よりよいコードを生成するために、C++コンパイラーはそれぞれ独自機能を追加するし、プログラマーは移植性を確保するために手動で関数やループの展開を行う。

論文では、叩き台となるための文法と機能を載せている。どうやら、alias_set()という文法でエイリアシングのルールに名前を付けて管理できるようだ。

N4151: TriviallyCopyable reference_wrapper

タイトル通り、reference_wrapperをtrivially copyableであることを保証する変更。つまり、コピーにはバイト列をコピーするだけでよいという保証になる。small buffer optimizationのために重要。

[PDFをハンドルしたくない] N4152: uncaught_exceptions

bool std::uncaught_exception()は、現在、例外がなげられてまだハンドラーが有効になっていない場合、わかりやすく言えば、キャッチされていない例外がある場合にtrueを返す。

これは、デストラクターがstack unwindingの結果呼び出されたかどうかの判断に使える。

class X
{
    ~X()
    {
        if ( std::uncaught_exception() )
            // stack unwindingの結果呼ばれた
        else
            // それ以外のデストラクター呼び出し
    }
} ;

int main()
{
    {
        X x ;
        // ブロックスコープを抜けたことによるデストラクター呼び出し
    }

    try {
        X x ;
        throw 0 ;
        // stack unwindingによるデストラクター呼び出し
    } catch( int e ) { }

}

問題は、uncaught_exceptionは、キャッチされている例外があるかどうかでしか判断しない。stack unwindingの結果実行されたデストラクターの中では、かならずtrueを返してしまう。これは、stack unwinding以外の方法で呼び出されたデストラクターも含まれる。

struct Y
{
    ~Y()
    {
        X x ;
        // X::~Xの中でuncaught_exceptionはtrueを返す
    }
} ;

また、stack unwindingの結果呼ばれたデストラクターの中で更に例外を投げた結果として呼ばれたデストラクターも区別できない。

struct Y
{
    ~Y()
    {
        try {
            X x ;
            throw 0 ;
        } catch( ... ) { }
    }
} ;

このため、前回の論文、N3614では、stack unwindingの結果呼び出された直接のデストラクターのみtrueを返す関数を追加しようという提案であった。

この論文では、int uncaught_exceptions()という関数を追加し、現在有効な例外の数を返すようにするという提案をしている。

[PDFをかろうじて使う必要がある画像埋め込み] N4153: 2015-02 LWG Meeting Invitation and Information

2015年2月23日から27日にドイツのケルンで行われるライブラリWG会議の案内

[PDFを使う必要がない] N4154: Operator assert

assert演算子の提案。

assertはプリプロセッサーマクロとして実装されているが、これはコンパイラーが解析して最適化のヒントとして使うのに都合が悪い。そこで、assert式を提案している。assertはキーワードになる。NDEBUGマクロなどの既存の意味は維持される。

CPPを廃止することを考えれば、現在CPPで提供されているあらゆる機能への代替機能が必要なので、わからないでもないが、コア言語でサポートすることだろうか。

N4155: Non-member size() and more (Revision 1)

非メンバーとしてsize()関数を追加する提案のN4017に対し、差分的な変更で、front()とback()のサポートを除去し、initializer_listに対してdata()とempty()をサポートする提案。

[PDFは軽量ではない] N4156: Light-Weight Execution Agents Revision 3

並列実行ライブラリの実行媒体(execution-agent)として、std::threadより軽量な実行単位について、std::threadのような強力なforward progress保証などがないという弱い保証厳密に規定している。

[PDFを緩和する必要はない] N4157: Relaxing Packaging Rules for Exceptions Thrown by Parallel Algorithms

並列実行版の<algorithm>は、複数の例外をexception_listでまとめて投げるが、これをまともに実装しようとすると、実装がやたらと複雑になる上に、ユーザーにそれほど利点をもたらさない。

そのため、例外がひとつだけの場合は、exception_listにいれずにそのまま投げることを許可する提案。

[破壊されるべきPDF] N4158: Destructive Move (Rev 1)

破壊的ムーブを行う関数テンプレートを追加する提案。

現在、標準ライブラリのムーブ後のオブジェクトは、未規定の状態にはなるものの、そのオブジェクトが提供する操作が動かなければならないと規定されている。これはつまり、ムーブ後にも最低限のストレージを動的に確保するなどの処理を行わなければならず、例外を投げるかもしれない。また、どうせ使わないオブジェクトであれば、その処理自体がオーバーヘッドになる。破壊的ムーブであれば例外を投げないムーブを提供できるクラスもある。


template <class T>
void uninitialized_destructive_move(T* from, T* to) noexcept ;

fromはムーブ元のオブジェクトへのポインターを指し、toはT型のオブジェクトを構築できるサイズを持った未初期化のストレージを指す。


T x ;
alignas(T) char buf[sizeof(T)] ;

std::experimental::fundamentals_v2::uninitialized_destructive_move( &x, buf ) ;

破壊的ムーブが例外を投げずに行えるかどうかを判断するtype traitsである、is_nothrow_destructive_movableも提案されている。


std::experimental::fundamentals_v2::is_nothrow_destructive_movable<T>::value ;

また、単なるバイト列のコピーだけでムーブできる、trivially destructive moveという概念も導入されている。これを調べるis_trivially_destructive_movableと、実際のその処理を行うuninitialized_trivial_destructive_moveも追加されている。

また、配列用に、uninitialized_destructive_move_nも提案されている。

N4159: std::function and Beyond

std::functionの設計は、2001年頃のboost::functionの実装を元にしており、最新のC++としてはやや痒いところに手が届かない部分がある。

std::functionのoperator ()はconst修飾されているが、内部に保持する関数オブジェクトは非constに呼び出す。これは標準ライブラリに要求される挙動に反するし、結果としてデータ競合を引き起こしうる動作になるが、std::functionの文面を解釈する限りそう解釈するしかない。

std::functionは、関数オブジェクトがコピー可能であることを要求している。コピーはできないがムーブならばできる関数オブジェクトを扱うことはできない。

C++14では初期化キャプチャーが追加されたので、ムーブしかできないオブジェクトを値キャプチャーしたlambda式というものが書けるようになり、ますますこの制限が問題になる。

std::functionは、関数オブジェクトをlvalueとして関数呼び出しする。つまり、operator ()にrvalueリファレンス修飾子がついている関数オブジェクトを呼び出すことはできない

// std::functionで扱えないクラス
struct rvalue_callable
{
    void operator () () const && ;
} ;

std::functionの問題を修正するには、互換性を壊す変更をするしかない。さもなければ、std::functionのoperator ()は例外的な挙動をすることを明示的に文面に残すかだ。

ドワンゴ広告

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

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

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