江添亮
株式会社ドワンゴ
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円
コア言語を不必要なまでに詳細に解説
何でも質問してください