江添亮
株式会社ドワンゴ
C++標準化委員会 委員
ボルダリング グレード4級
2015-09 pre-Konaから抜粋
いくつかの機能は
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は戻り値にoperator !を適用して返す関数オブジェクトのラッパーを返す
bool f( ) { true ;} int main() { auto nf = not_fn( &f ) ; nf() ; // false }
もともとはLibrary Fundamentalsに入っていた
独立していて便利
これ単体だけ直接C++17に追加する
変数テンプレートによるtraitsラッパー
::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
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に入れる
アグリゲート初期化の拡張
機械的な転送コンストラクターを書くのはダルい
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を値キャプチャー
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
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
強いtypedef
異なる型として区別される
文法はまだ議論中
typedef int a ; newtype int b ; std::is_same_v<a, int> ; // true std::is_same_v<b, int> ; // falase
オーバーアラインされたメモリの動的確保
現行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 ) ;
フラットコンテナー
Boost.ContainerのFlat Containerを標準ライブラリに提案
flat_map, flat_set, flat_multimap, flat_multiset
連続したストレージ上に確保される連想コンテナー
アルゴリズム案1
常に要素をソートしておく
要素の追加、検索、削除にバイナリサーチ+要素のシフト分の時間がかかる
アルゴリズム案2
ヒープ構造を構築
要素の検索の歳のキャッシュ効率が良い
委員会ではこちらのほうが注目が高い
内部アルゴリズムの指定やアルゴリズム別のコンテナーも検討されている
遅延追加
要素の追加を遅延させるとコストを低下させることができる。
しかし、意図せぬ挙動でユーザーを驚かせるかもしれない
[[unused]], [[nodiscard]], [[fallthrough]]
[[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
以上
constexpr_if
旧static_if
template < typename T > T sum( T && t ) { constexpr_if ( has_func<T>::value ) { t.func() ; } else { func( t ) ; } }
この記事はドワンゴ勤務中に書かれた
ドワンゴは本物のC++プログラマーを募集しています
C++11/14コア言語がアスキードワンゴから出版
価格3800円
コア言語を不必要なまでに詳細に解説
何でも質問してください