2016-11-14

C++標準化委員会の文書: P0471R0-P0479R0

P0471R0: Single argument std::inserter

引数が1つのinserterを追加する提案。

vectorの要素をすべてsetにコピーする場合、inserterを使うと便利だ。

std::vector<int> vector ;
std::set<int> set ;

std::copy( begin(vector), end(vector), std::inserter( set, begin(set) ) ) ;

問題は、inserterの第2引数は、この場合何の意味も果たしていないということだ。第2引数はbegin(set)でもend(set)でも挙動は変わらない。ならば、inserterに引数を1つしか取らないオーバーロードを追加すべきではないか。

引数を1つしか取らないinserter( Container c )は、inserter( c, c.begin() )と同じ意味になる。

これは入るべきだ。

P0472R0: Put std::monostate in <utility>

<variant>にあるstd::monostateを<utility>に移動する提案。

monostateは1値を表現する型である。monostateは1種類の状態しか取らない。monostateは比較演算子をサポートしている。この特性は汎用的に便利なので、variant以外の場面でも使いたい。monostateだけを使うのに<variant>に依存させるよりは、<utility>に移したい。

利用例としては、テンプレートコードがうっかり型独自の操作に依存しているかどうかを調べるテストの入力として、futureなどで値を持たないことを意味するためにvoidを渡しているが、voidは特殊な特性があり扱いづらいため、voidの代わりとして渡すことが上げられている。

voidは完全型にする提案が上がっているが、まあ、別にutilityでもいい気はする。

P0473R0: + for std::vector concatenation

operator +とoperator +=でvectorを連結できる機能の提案。まるでbasic_stringのようだ。

int main()
{
    std::vector<int> v1 = { 1, 2, 3 } ;
    std::vector<int> v2 = { 4, 5, 6 } ;

    auto v3 = v1 + v2 ;
    v3 += v1 ;

    // v3の中身は{1,2,3,4,5,6,1,2,3}
}

いまさら? 確かに、vectorの連結はよく行う処理ではあるので、簡単にかけるのは便利なのだろうが。

P0474R0: Comparison in C++: Basic Facilities

partial, weak, total orderの3種類の比較を提供する関数群の提案の文面案

P0475R0: LWG 2511: guaranteed copy elision for piecewise construction

C++17でコピー省略が必須になったので、CopyConstructible要件を付けなくて良くなった箇所から要件を取り除く提案。

P0476R0: P0476r0: Bit-casting object representations

ビット列を指定した型として解釈するライブラリ、bit_cast<To>(From)の提案。

ビット列を型として解釈するには、reinterpret_castやunionがよく使われるが、これには未定義の挙動の問題がある。規格に詳しいプログラマーはstd::aligned_storageとmemcpyを使うが、memcpyはconstexprではない。そこで、constexprなビット列キャストライブラリを追加する。

[PDF] P0477R0: std::monostate_function<>

関数ポインター、メンバー関数へのポインターのラッパーライブラリ、std::monostate_functionの提案。


void f() { }

struct X
{
    void f() { }
} ;

int main()
{
    std::monostate_function<&f> f1 ;
    f1() ; // fを呼び出す

    std::monostate_function< &X::f > f2 ;
    X x ;
    f2( x ) ; // X::fを&xをthisとして呼び出す
}

C++17から新しく入った非型テンプレートパラメーターに対するautoを使っているので、テンプレート実引数には値を指定するだけでよい。


template < auto Callable >
struct monostate_function
{
    template < typename ... Types >
    constexpr
    auto operator () ( Types ... args )
    noexcept( std::invoke( Callable, std::declval<Types>... ) )
    {
        return std::invoke( Callable, std::forward<Types>(args)... ) ;
    }
} ;

なぜこんなライブラリが必要なのか。関数ポインターを呼び出したければそのまま呼びだせばいいのではないか。一見するとそう思うかもしれない。このライブラリの目的は、非型ではなくて型を受け取るテンプレートに関数ポインターを渡すためのものだ。

当然ながら、関数ポインターは値である。値は型ではない。型ではないものは型テンプレートパラメーターには渡せない。

たとえば、setの比較関数に独自のcompare関数を使いたいとする。以下のように書ける。


struct UserData { /* データ */ } ;
// UserData型を比較する既存の関数
bool compare_UserData( UserData const & a, UserData const & b ) ;

int main()
{
    std::set< UserData, decltype( &compare_UserData )> set( &compare_UserData ) ;
}

これは甚だ冗長だ。かならずcompare_UserDataを呼び出す型ががあれば、setのコンストラクターに関数ポインターを渡す必要はない。そこで、以下のように書ける。

struct call_compare_UserData
{
    bool operator ()( UserData const & a, UserData const & b )
    {
        return compare_UserData( a, b ) ;
    }
} ;

int main()
{
    std::set< UserData, call_compare_UserData > set ;
}

しかし、これでは関数ごとにクラスをつくって引数を転送するだけのボイラープレートコードを書かなければならない。そこで、monostate_functionの登場だ。

std::set< UserData, std::monostate_function< compare_UserData > > set ;

このように簡単に書ける。

また、unique_ptrにデリーターをわざわざ書かなくても、引数さえあうのであれば、monostate_functionが使える。例えば、mallocで確保したメモリはfreeで解放しなければならないが、monostate_functionを使えば、以下のようにunique_ptrのデリーターが書ける。

int main()
{
    std::unique_ptr<int, std::monostate_function<&std::free> >
        ptr( reinterpret_cast<int*>(malloc(sizeof(int))) ) ;
}

なかなか悪くない。

[PDF] P0478R0: Template argument deduction for non-terminal function parameter packs

Variadic Templatesが最後のテンプレートパラメーターではなくても、実引数推定を行えるように制限を緩和する提案。

以下のように書けるようになる。

template < typename ... A, typename B > struct A { } ;
template < typename A, typename ... B, typename C > struct B { } ;

Variadic Templatesはテンプレート内に1つでなければならない。

これにより、パラメーターパックの最後の要素を取得したり、引数の順序に自由度が出せたりする。

これは当然入るべきだ。

P0479R0: Attributes for Likely and Unlikely Branches

条件分岐の分岐が実行される頻度をヒントとして与える属性、[[likely]]と[[unlikely]]の提案。

条件分岐で、どちらかの分岐がほぼ実行されることが事前に予測できる場合、これをコンパイラーに伝えると、よりよいコードを生成できる。また、現在の深いパイプライン、高度な分岐予測になったアーキテクチャ上でも、条件分岐の結果があらかじめ予想できるのは都合がいい。

GCCとClangには、__builtin_expectedという拡張機能がある。これを使って、ある分岐の選択が期待できるかどうかをコンパイラーにヒントとして与えることができる。既存のコードを調べたところ、__builtin_expectedを使うコードのほとんどは、

#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

このふたつのマクロだけで用が足りる。現在、likelyとunlikelyを最も使っているのはおそらくLinuxカーネルで、likelyを3000回以上、unlikelyを14000回以上使っている。他にも、Mozillaはlikelyを200回以上、unlikelyを7000回以上使っている。chromiumも数百回以上likelyとunlikelyを使っている。

そこで、分岐が選ばれることが期待できる[[likely]]と、分岐が選ばれないことが期待できる[[unlikely]]というふたつの属性を追加する。これにより、コンパイラーにヒントを与えることができる。

この属性は、conditionに記述できる。

// エラーは通常起こらない
if ( [[unlikely]] check_error() )
{
    do_error_log() ;
}

// 計算は通常ならばすでに終わっている。
if ( [[likely]] is_calculation_completed() )
{
    show_result() ;
}

入るべきだ。

ドワンゴ広告

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

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

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

2 comments:

Anonymous said...

vectorの結合は今更感が強いですねぇ。まぁ、たまにやるのであったらあったで便利ですけど。
likelyなどは、自分は通常コンパイラさんにお任せですけど、そんなに変わるもんですかねぇ。
モノステートは私の頭が悪くて理解できませんでした。そういうことあんまりしないですし。Orz

opbest said...



That is a very good tip especially to those new to the blogosphere.
Short but very accurate info… Appreciate your sharing this one. A must read post.

My web site - 부산오피

(freaky)