2015-07-07

C++標準化委員会の文書 2015-05 post-Lenexaのレビュー: N4483-N4499

[PDF注意] N4483: Read-copy-update

Linuxカーネルで多用されているRCU(Read-Copy-Update)の解説文書。

N4484: C++ Standard Library Active Issues List
N4485: C++ Standard Library Defect Report List
N4486: C++ Standard Library Closed Issues List

標準ライブラリの既知の問題集

N4487: Constexpr lambdas

constexpr lambdaの提案。

lambda式のクロージャーオブジェクトのデータメンバー、つまりキャプチャする変数の初期化が定数式ならば、定数式のなかで利用できる。

クロージャーオブジェクトがリテラル型ならば、定数式の中で評価できる。

constexpr指定子をlambda式に記述できる。

auto f = []( int x ) constexpr { return x ; }
constexpr int x = f( 1 ) ;

constexpr指定子は省略できる。その場合、lambda式がconstexpr関数の条件を満たせば、関数呼び出し式はconstexprになる。

auto f = []( int x ) { return x ; }
constexpr int x = f( 1 ) ;

クロージャー型の関数ポインター型への変換関数はconstexprになる。

この提案は、lambda式を未評価式の中に記述することを提案していない。つまり、テンプレート仮引数のデフォルトテンプレート実引数に使ってSFINAEトリックに使うことはできない。

Clangベースの実験的実装が公開されている。

https://github.com/faisalv/clang/tree/constexpr-lambdas

N4488: Responses to PDTS comments on Transactional Memory, version 2

トランザクショナルメモリーTSに対するPDTSコメントに対する返答。

日本からは、JP1として、トランザクショナルメモリーがmath.hに対してトランザクショナルセーフを要求しているので、対応のためにパフォーマンスの低下が懸念されるとコメントを送ったが、回答は、math.hは明確に除外されているとしてリジェクトされた。

math.hが明確に除外されている文面について、日本は把握していない。

JP2として、関数のローカルstatic変数の初期化がアトミックに行われるべきだと主張したが、これもトランザクショナルセーフで定める範囲ではないとしてリジェクトされた。

N4489: WG21 2015-04-24 Telecon Minutes

4月24日に行われた電話会議の議事録。

N4490: WG21 2015-05 Lenexa Minutes

5月4日から9日にかけてLenexaで行われた会議の議事録。

[PDF注意] N4491: PL22.16 2015-05 Lenexa Minutes (Draft)

N4490のドラフトと見える。内容はほぼ同じ。

[PDF] N4492: Thoughts about C++17

Bjarne StroustrupによるC++17の考察。

ドラフト版の翻訳をすでに行っている。

本の虫: D4492: Bjarne StroustrupによるC++17の考察の翻訳

N4494: Multidimensional bounds, offset and array_view, revision 6

連続したストレージを多次元配列に見せかけるarray_viewライブラリの提案。前回からの変更点は、一部の識別子の変更や文面の変更など。

N4495: Smart Objects, an alternative approach to overloading operator dot

operator .をオーバーロードする機能の提案。N4477とは設計が違う。

N4477のoperator .は、何らかのリファレンスを返すものであった。

// N4477提案
struct Callable
{
    template < typename ... Types >
    void operator () ( Types ... args ) { }
} ;

struct X
{
    Callable c ;

    Callable & operator . ( )
    {
        return c ;   
    }
} ;

int main()
{
    X x ;
    x.foo() ;
    x.bar( 1, 2, 3 ) ;
}

何らかのリファレンスを返さなければならないということは、結構面倒な制約だ。第一、そのリファレンスが保持された場合どうするのか? その寿命がいつまで存続するのかは、operator .の実装次第だ。

// 寿命はいつまで?
auto ref = x.member ;

N4495では、operator .にコンパイラーが生成する関数オブジェクトが実引数として渡される。

たとえば、

x.some_name ;

と書いて、xにsome_nameというメンバーが存在しない場合、

x.opeator .( synthesized_function_type{} ) ;

というコードに変換される。synthesized_function_typeはコンパイラーにより生成される型で、以下と同等のものになる。

struct synthesized_function_type {
    template<class T>
    auto operator()(T&& t) -> decltype(t.some_name) noexcept(t.some_name) {
        return t.some_name;
    }
};

some_nameに対してintの値を返したい場合、これを受けるxのoperator .は、テンプレートを使って以下のように書ける。

struct some_name_t
{
    int some_name = 0 ;
} ;

struct X
{
    some_name_t data ;

    template < typename T >
    auto operator . ( T && t )
    {
        return t( data ) ;
    }

} ;

メンバー関数呼び出しの場合、実引数に渡す式は、コンパイラーが関数オブジェクトを構築する前に評価される。rvalue性は正しくforwardされる。


x.some_name( a, foo(), foo() ) ;

は、以下のように変換される。

x.operator.(synthesized_function_type{a, foo(), foo()});

コンパイラーによって生成されるsynthesized_function_typeは、以下のようになる。

struct synthesized_function_type {
    // `a' or `foo' may not be visible in that context
    // used here lexically just for demonstration purposes
    typedef decltype((a)) T0;
    typedef decltype(foo()) T1;
    typedef decltype(foo()) T2;
    T0 a0;
    T1 a1;
    T2 a2;

    template<class T>
    auto operator()(T&& t) -> decltype(t.some_name(static_cast<T0&&>(a0), static_cast<T1&&>(a1), static_cast<T2&&>(a2))) noexcept(t.some_name(static_cast<T0&&>(a0), static_cast<T1&&>(a1), static_cast<T2&&>(a2))) {
        return t.some_name(static_cast<T0&&>(a0), static_cast<T1&&>(a1), static_cast<T2&&>(a2));
    }
};

コンパイラーによって生成される関数オブジェクトに、メンバー名の文字列やメンバー関数であるかどうかを取得できるリフレクション機能をつける拡張も将来的に追加できる設計となっている。例えば、リフレクションをサポートする例として、以下のような関数オブジェクトが生成される。

struct synthesized_function_type {
    template<class T>
    auto operator()(T&& t) -> decltype(t.some_name) noexcept(t.some_name) {
        return t.some_name;
    }

    static constexpr const char* name() { return "some_name"; }
    static constexpr bool is_member_function = false;
};

うーむ・・・やりたいことはわかるのだが、この設計はもう少しなんとかならないものか。

N4496: WG21 2014-11 Urbana Minutes (revision 1)

2014年11月3日から8日にかけてUrbanaで行われた会議の議事録の改訂版とみられる。

[PDF注意] N4497: PL22.16 2014-11 Urbana Minutes (Final)

上記議事録の最終校のようなタイトル。

N4498: Variadic lock_guard (Rev. 2)

lock_guardをVariadic Templatesを使い、任意個のロックを管理できるようにしようという提案。

これまで、複数のLockable要件を満たす型をロックするのに

std::mutex m1, m2 ;

void f()
{
    std::lock( m1, m2 ) ;
    std::lock_guard< std::mutex >( m1, std::adopt_lock ) ;
    std::lock_guard< std::mutex >( m2, std::adopt_lock ) ;
}

のようなコードを書かなければならなかったが、N4498提案では、

void f()
{
    std::lock_guard< std::mutex, std::mutex >( m1, m2 ) ;
}

と書くだけでよい。

[PDF注意] N4499: Draft wording for Coroutines (Revision 2)

コルーチンのドラフト。まだまだ色々変更がありそうなので詳細に読む気にならない。

ドワンゴ広告

最近、社内で計算方法が厳密に規定されているdistributionクラスが標準ライブラリに欲しいという話がでている。デバッグ目的で全く同一の乱数列を移植性の高い方法で生成したい。C++の乱数ライブラリは、生の乱数を生成するエンジンと、乱数を目的の範囲の値に分布させるディストリビューションに分かれている。エンジンクラスの乱数の生成方法は厳密に規定されているが、ディストリビューションクラスの実装方法は規定されていない。

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

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

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

2 comments:

Anonymous said...

ディストリビューションクラスですが、メルセンヌツイスターレベルの乱数なら単なる余剰でもそれなりに質のいい乱数が取れるんじゃないかと思います。
乱数の重複を許す時にたまに使いますが、反証は聞いたことがありません。もちろんアマレベルの話ですよ。

Anonymous said...

N4487ですとconstexprはlambda式のプロトタイプ部分の後ろのようですが、普通の関数では前ですよね。
unified function syntaxではどうなるのでしょうか?