2015-07-22

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

N4531: std::rand replacement, revision 3

めでたくdeprecatedされたrandの代わりになるお手軽に使える乱数ライブラリ、randintの提案。

randintは関数テンプレートとなっていて、整数型に対して特殊化できる。

// -10から10までの間のint型の乱数を生成
auto a = std::randint( -10, 10 ) ;
// 0Lから10Lまでの間のlong型の乱数を生成
auto b = std::randint( 0L, 10L ) ;

randintの乱数を生成するために、スレッドごとにdefault_random_engine型のオブジェクトが予測不可能な状態に初期化される。つまり、プログラムの実行ごとにrandintの結果は変わる。

乱数エンジンの状態を再び予測不可能な状態にするには、std::reseed()を呼び出す。

reseedに値を指定すると、状態を予測可能にできる。これは、デバッグ目的に使える。

その他に、乱数生成を関数オブジェクトとして引数に取らない版のshuffle, sampleアルゴリズムが追加される。

N4532: Proposed wording for default comparisons

クラスにデフォルトの比較演算子を生成する提案の文面案。

クラスにvirtual基本クラス、virtualメンバー関数、mutableメンバー、ポインター型の非staticデータメンバー、ユーザー提供されたコピー/ムーブコンストラクターと代入演算子はデフォルトの比較演算子は生成されない。

N4533: Make exception-specifications be part of the type system, version 3

例外指定を型の一部に含める提案。

N4534: Data-Invariant Functions (revision 3)

サイドチャネル攻撃を防ぐためのライブラリ。

value<T>の形で利用して、値の比較ができる。値によって外部から観測可能な物理的挙動(実行時間やメモリアクセスパターンなど)が異なる場合、SONO物理的挙動を外部から観測することで、値が推測できてしまう。valueライブラリは、物理的挙動の差をなくしてくれる。

今回の提案では、barrierが入っていたり、イテレーターの範囲の値がそれぞれ等しいか比較するequal、条件でどちらかのイテレーターの範囲をコピーするcopy_conditional、イテレーターを進めるlookupが入っている。

N4535: Feature-testing preprocessor predicates for C++17

機能テスト推奨で提案されていた__has_includeと__has_cpp_attributeをC++17に追加する提案。

N4536: clamp: An algorithm to 'clamp' a value between a pair of boundary values (Draft) -

clamp( value, min, max )の提案。

#include <functinal>

int main()
{
    auto a = std::clamp( 5, 1, 10 ) ; // 5
    auto b = std::clamp( 11, 1, 10 ) ; // 10
    auto c = std::clamp( -1, 1, 10 ) ; // 1
}

ある値をある範囲に収めることはプログラミングではよくある処理である。標準にその方法がない場合、非統一的な方法で書かれてしまう。例えば以下のように。


auto clamped_value = std::min( std::max( value, min_value ), max_value );

また、範囲版も提案されている。


std::vector<int> v = { 1, 2, 3, 4, 5, 6, 7, 8, 9 } ;

// { 3, 3, 3, 4, 5, 5, 5, 5, 5 } 
std::clamp_range( v.begin(), v.end(), v.begin(), 3, 5 ) ;

ちなみに、ドワンゴ社内ではclamp( min, value, max )という引数の並びを好む人もいるようだ。また、clampを関数オブジェクトを返す関数にして、clamp(min, max)(value)などという文法を好む人もいるようだ。

N4537: Adding Symmetry Between shared_ptr and weak_ptr

weak_ptrに欠けているshared_ptrの機能を追加する提案。

shared_ptr<vector<int>>のような型から、vectorのある要素へのポインターを返すweak_ptrがほしいとする。shared_ptrにはそのための機能がある。所有権を管理するポインターと、getで変えるポインターをコンストラクターの引数で別々に指定することができるのだ。

shared_ptr<int> first_elt_strong(const shared_ptr<vector<int>>& vec)
{
    return shared_ptr<int>(vec, &vec->at(0));
}

さて、戻り値に返すポインターに、それほど強い参照が欲しくない場合、戻り値の型をweak_ptrに変えることが考えられる。すると、以下のようになる。

weak_ptr<int> first_elt_strong(const shared_ptr<vector<int>>& vec)
{
    return shared_ptr<int>(vec, &vec->at(0));
}

これは動く。ただし、必要以上に非効率的である。まず、shared_ptrを作るので参照カウンターがインクリメントされる。次に、weak_ptrが作られるので、weak参照カウンターがインクリメントされる。そして、shared_ptrが破棄されるので、参照カウンターがデクリメントされる。

実に3回もの不可避なメモリアクセスが発生しているではないか。技術的には、メモリアクセスはweak参照カウンターの一回だけにできるはずである。しかし、weak_ptrのコンストラクターにはshared_ptrのコンストラクターがない。そのため、shared_ptrを経由しなければweak_ptrが作れない。

この問題を解決するために、提案ではweak_ptrにも同等のコンストラクターを追加している。

weak_ptr<int> first_elt_strong(const shared_ptr<vector<int>>& vec)
{
    return weak_ptr<int>(vec, &vec->at(0));
}

もうひとつの問題は、shared_ptrからweak_ptrを作り出すには、キャストが必要だということだ。以下の例を考えてみる。

template < ObjectType >
void f( ObjectType & obj )
{
    auto sp = obj.get_shared_ptr() ;
    auto wp = weak_ptr<ObjectType>(sp) ;
    // ...
}

このコードは、あまりジェネリックではない。もし、obj.get_shared_ptr()の返すshared_ptrが、shared_ptr<ObjectType>ではなく、shared_ptr<BaseClassOfObjectType>であるかもしれない。あるいは、std::shared_ptrではなく、独自のcustom_shared_ptrを返すかもしれない。

そもそも、このコードでは明示的に型を指定している部分は、弱体化(weakening)している部分だけである。weakeningに明示的で具体的な型を必要とするのはおかしい。

そのため、論文ではshared_ptrにweak_ptrを返すunlockメンバー関数の導入を提案している。

template < ObjectType >
void f( ObjectType & obj )
{
    auto sp = obj.get_shared_ptr() ;
    auto wp = sp.unlock() ;
    // ...
}

weak_ptrからshared_ptrを作り出す既存のメンバーとしてlockがあるので、その逆としてunlockだ。

[PDF注意] N3538: Technical Specification for C++ Extensions for Concurrency

Concurrency TS。非同期処理のための様々なライブラリの改良と追加。

N4539: C++ Standard Evolution Active Issues List
N4540: C++ Standard Evolution Completed Issues List
N4541: C++ Standard Evolution Closed Issues List

C++の新機能提案に対する既知の問題集

[PDF注意] N4542: Variant: a type-safe union (v4).

型安全unionライブラリ、variantの提案。boostのものとはvariantのネストを認めないなどで違っている。

[PDF注意] N4543: A polymorphic wrapper for all Callable objects

std::functionのコピー不可能版のstd::unique_functionの提案。

std::functionは関数オブジェクトがコピー可能であることを要求する。コピー可能ではない型は格納できない。std::unique_ptrはコピー不可能な型を格納できる。

コピー不可能であること以外は、std::functionと同等のインターフェースを持っている。std::functionでstd::unique_functionを初期化することはできる。逆はできない。

N4544: October 2015 WG21 Meeting (Kona)

2015年10月にハワイで行われる会議の案内。

ドワンゴ広告

先週の土曜日に、ドワンゴのセミナールームでTopCoderの競技プログラミングの予選、TCO15 in Tokyoが開催された。

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

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

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

4 comments:

  1. ランダムが手軽に使えるようになるのは好ましいですね。
    アルゴリズムはメルセンヌツイスターになるのでしょうか。
    自分は基本的にそれで問題ありませんが。
    それと、clampになったんですね。
    だいぶん前にどこかに書いたような気がするのですが、やっと入るんですね。
    これがあるとアクションゲームで自分を中心とした、区切られた範囲爆撃とかできるんですよね。
    並びは別に気にしません。使ってればなれるでしょうし。
    まぁ、歓迎いたします!
    ただ、ヘッダーがファンクショナルなんですね。ふむ。

    ReplyDelete
  2. > variantのネストを認めない
    以前も同じ指摘を受けていましたが、Recursive variantと混同していませんか?

    ReplyDelete
  3. typo?

    ×
    #include

    #include

    ReplyDelete
  4. ↑1 エスケープされた……

    ×
    #include functinal

    #include functional

    ReplyDelete

You can use some HTML elements, such as <b>, <i>, <a>, also, some characters need to be entity referenced such as <, > and & Your comment may need to be confirmed by blog author. Your comment will be published under GFDL 1.3 or later license with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.