最新のC++標準化委員会の文書

江添亮

株式会社ドワンゴ

C++標準化委員会 委員

ボルダリング グレード4級

文書

2015-09 pre-Konaから抜粋

時代遅れの機能削除

P0001R0

P0002R0

P0003R0

P0004R0

時代遅れの機能

いくつかの機能は

deprecated(廃止予定)の扱いを受けている

設計が悪い

よりよい機能がある

時代にそぐわなくなった

registerキーワード廃止

C++11でdeprecated

C++17で完全に削除

bool型に対するoperator ++の廃止

C++98でdeprecated

C++14で廃止検討されたが却下

後置記法の代替としてstd::excangeを追加

C++17で廃止

「20年も準備期間を与えたんだからもういいだろ」

動的例外指定の廃止

実際の使い勝手はあまり良くなかった

1998年の時点で使うべきではないというお作法が確立

C++11でdeprecated

C++17で廃止

昔のiostreamのtypedef名を削除

ios_baseにあるio_stateとかopen_modeとかそのへん

メモリー 正直あんまり興味なし

C++98でdeprecated

C++17で廃止

not_fn

P0005R0

not_fnは戻り値にoperator !を適用して返す関数オブジェクトのラッパーを返す

bool f( ) { true ;}

int main()
{
    auto nf = not_fn( &f ) ;

    nf() ; // false 
}

もともとはLibrary Fundamentalsに入っていた

独立していて便利

これ単体だけ直接C++17に追加する

変数テンプレートによるtraitsラッパー

P0006R0

::valueを書かなくてもすむ

// 面倒
constexpr bool a = std::is_same<int, int>::value ;
// 簡単
constexpr bool b = std::is_same_v<int, int> ;

C++14の変数テンプレート

みんなもう使ってるね?

template < typename T, typename U >
constexpr bool is_same_v = is_same<T, U>::value ;

as_const

P0007R0

T &をT const &にキャストできる

std::string str = "hello" ;
std::string const & cstr = std::as_const( str ) ;

代替案より便利

// 型を手で書く必要がある
const_cast< const std::string & > ( str ) ;
// 汎用的だが長すぎる
const_cast< std::add_const< decltype( str ) >::type >( str ) ;

Library Fundamentalsに入っている

先行してC++17に入れる

アグリゲート初期化の拡張

P0017R0

機械的な転送コンストラクターを書くのはダルい

struct user
{
    std::uint32_t id ;
    std::string name ;

    user( std::uint32_t id, std::string const & name )
        : id( id ), name( name ) 
    { }
} ;

C++11にはアグリゲート初期化がある


struct user
{
    std::uint32_t id ;
    std::string name ;
} ;

user Bjarne{ 85, "Bjarne" } ; 

しかし

アグリゲート初期化は基本クラスがある場合、できない

struct unique_id
{
    std::uint32_t id ;
} ;

struct user : unique_id
{
    std::string name ;
} ;

たとえ基本クラスが

デフォルト構築可能であっても

空であっても

できない

struct human_tag { } ;

struct user : human_tag
{
    std::uint32_t id ;
    std::string name ;
} ;

単に引数の転送を行うだけの

単調なコード(boilarplate code)

書きたくない

アグリゲート初期化を使いたい

基本クラスを最初の要素としてしまおう。


user Bjanrne { {85}, "Bjarne" } ;

多重継承は順番通りに。


struct Id { std::uint32_t id ; } ;
struct Name { std::string name ; } ;

struct user : Id, Name { } ;

user Bjarne{ {85}, {"Bjarne"} } ;

lambda式で*thisを値キャプチャー

P0018R0

lambda式はthisポインターをキャプチャする。

struct X
{
    int data ;

    std::function< int () > f()
    {
        // this->data
        return [-]{ return data ; } ;
    }
} ;

C++14では汎用lambdaキャプチャーが追加された

コピーできる

return [ data = data ]
{
    return data ;
}

しかし

*this(クラスオブジェクト)を値コピーしたい

並列実行

heterogeneousなメモリシステム

NUMA

の発展により

データをクロージャーに食わせるより

データにクロージャーを付与させるほうが都合がいい

汎用lambdaキャプチャーで書く場合

return [=, tmp = *this]
{
    return tmp.data ;
}

問題点

thisポインターがキャプチャされる

tmp.を書き忘れるとthis->dataになる。

変数事にtmp.と書くのは極めて面倒

*thisを値キャプチャできるようにしよう

解決案1

=の挙動を変える

挙動の変更

互換性ガー

新しいキャプチャーデフォルト、* を作る

=とほぼ同じ意味

thisをコピーキャプチャする

// *thisと明示的に書くこともできる
return [*]
{
    return data ;
}

multi-range-based for loop

P0026R0

C++11にはいったrange-based for loopは便利だ

std::vector< int > v ;
for ( auto && elem : v )
{
    DoSomething( elem ) ;
}

しかし

現実には、複数のコンテナーを同時に回したいことがよくある

std::vector<int> v1. v2 ;

for ( auto iter1 = v1.begin(), iter2 = v2.begin() ;
    iter1 != v1.end() && iter2 != v2.end() ;
    ++iter1, ++iter2 )
{
    DoSomething( *iter1, *iter2 ) ;
}

それ、Boostでできるよ

template <typename Cont...>
auto multi_range(Cont&... containers)
{
    return {
        boost::make_zip_iterator(
            boost::make_tuple(containers.begin()...)
        ),
        boost::make_zip_iterator(
            boost::make_tuple(containers.end()...)
    ) };
} 

for (auto&& t : multi_range(v1, v2))
{
    DoSomething(t.get<0>(), t.get<1>());
} 

どれも面倒だ

for_each風のライブラリで対応できるか

あまりいいインターフェースが作れない

コア言語によるサポートが必要だ

multi-range-based for

任意個のrange-based forの組を書ける

for ( auto && e1 : v1 ; auto && e2 : v2 )
{
    DoSomething( e1, e2 ) ;
}

Named Types

P0027R0

強いtypedef

異なる型として区別される

文法はまだ議論中

typedef int a ;
newtype int b ;

std::is_same_v<a, int> ; // true
std::is_same_v<b, int> ; // falase

オーバーアラインされたメモリの動的確保

P0035R0

現行C++では

必要を上回るアライメント要求を

コンパイラーがサポートする必要はない

このコードが16バイトアライメントされたメモリを返す保証はない

class alignas(16) float4 { float f[4] ;} ;
float4 * new float4[1000] ;

オーバーアラインされたメモリを返す確保関数を規定しよう

void * operator new( std::size_t size,0 std::align_val_t alignment ) ;
void operator delete ( void * ptr, std::align_val_t alignment ) ;

フラットコンテナー

P0038R0

Boost.ContainerのFlat Containerを標準ライブラリに提案

flat_map, flat_set, flat_multimap, flat_multiset

連続したストレージ上に確保される連想コンテナー

アルゴリズム案1

常に要素をソートしておく

要素の追加、検索、削除にバイナリサーチ+要素のシフト分の時間がかかる

アルゴリズム案2

ヒープ構造を構築

要素の検索の歳のキャッシュ効率が良い

委員会ではこちらのほうが注目が高い

内部アルゴリズムの指定やアルゴリズム別のコンテナーも検討されている

遅延追加

要素の追加を遅延させるとコストを低下させることができる。

しかし、意図せぬ挙動でユーザーを驚かせるかもしれない

[[unused]], [[nodiscard]], [[fallthrough]]

P0068R0

[[unused]]

使われていないエンティティに対する警告を抑制する

int x ; // コンパイラーが未使用警告を出す
[[unused]] int x ; // 未使用警告は抑制される

[[nodiscard]]

関数に指定した場合、戻り値が無視されると警告する

[[nodiscard]] bool f() ;

int main()
{
    f() ; // 警告、戻り値が無視されている
   
    if( f() ) // OK
        DoSomething() ;
    else
        DoErrorHandling() ;
}

型に指定すると、型を返す関数が[[discard]]指定される

[[nodiscard]] enum struct Status { success, error } ;
Status TrySomething() ;

int main()
{
    TrySomething() ; // 警告
}

[[fallthrough]]

switchのcase分の前に指定する

caseを飛び越えた場合の警告がなくなる

switch( x )
{
    case 0 :
        DoSomething() ;
    case 1 : // 警告
        DoSomething() ;
        [[fallthrough]] ;
    case 2 : // 警告なし
        DoSomething() ;
}

to_string/to_wstring

以上

P0117R0

constexpr_if

P0128R0

旧static_if

template < typename T >
T sum( T && t )
{
    constexpr_if ( has_func<T>::value )
    {
        t.func() ;
    }
    else
    {
        func( t ) ;
    }
}

宣伝

この記事はドワンゴ勤務中に書かれた

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

http://dwango.co.jp/recruit/

宣伝

C++11/14コア言語がアスキードワンゴから出版

価格3800円

コア言語を不必要なまでに詳細に解説

何でも質問してください