C++1zに採択された新機能
C++1zともC++17とも呼ばれているC++の次の規格には、まだ大きな機能は採択されていない。それでも、いくつかドラフト入りしている新機能はあるので、ここではその機能を紹介していく。
いつも通り、ここに書かれている内容はまだドラフト段階の機能であり、今後変更されたり、取り除かれたりする可能性もある。
N3928: メッセージ無しstatic_assert
C++11で追加されたstatic_assertには、文法上、必ず文字列リテラルを記述しなければならなかった。
static_assert( expr, "Captain Obvious To the Rescue! expr is false.") ;
この文字列リテラルは、実装が診断メッセージ(例えばコンパイラーのエラーメッセージ)に使うことができる。しかし、C++14までは、文字列リテラルが文法上必須で、必ず記述しなければならなかった。
そこで、文字列リテラルを記述しなくても良い文法が追加された。
static_assert( expr ) ;
N4086: トライグラフの除去??!
トライグラフが文面から削除された。
N4051: テンプレートテンプレートパラメーターにtypenameキーワード
C++14まで、テンプレートテンプレートパラメーターの文法は、classキーワードしか使えなかった。
template <
template < typename T >
class U // typenameキーワードは不可
> struct X ;
typenameも使えるようになる。
template <
template < typename T >
typename U // typenameキーワードが使える
> struct X ;
N4295: Fold式
パラメーターパックの中身すべてに対して演算子を適用したい場合、再帰的なテンプレートを書く必要がある。例えば、引数をすべてoperator +で合計する関数テンプレートを書くと、以下のようになる。
template < typename T >
T sum( T && t )
{
return t ;
}
template < typename T, typename ... Types >
T sum( T && t, Types && ... args )
{
return t + sum( std::forward<Types>( args ) ... ) ;
}
いかにも面倒だ。やりたいことは、a1 + a2 + a3 + ... + aNということなのに、この記述はあまりにも冗長すぎる。
そこで提案されているのが、fold式だ。パラメーターパックpにたいして、(p + ...)と書くと、p1 + p2 + p3 + ... pNとパック展開してくれる。
fold式を使うと、以下のように書ける。
template < typename ... Types >
auto sum( Types && ... args )
{
return (args + ...) ;
}
fold式では、括弧は必須である。
template < typename ... Types >
auto sum( Types && ... args )
{
// エラー
return args + ... ;
}
fold式には、left foldとright foldが存在する。
(... op e)は、left foldである。(e1 op e2) op e3) op e4のように展開される。
template < typename ... Types >
auto sum( Types && ... args )
{
return (... + args) ;
}
int main()
{
// ((1 + 2) + 3) + 4
sum( 1, 2, 3, 4 ) ;
}
(e op ...)は、right foldである。e1 + (e2 + (e3 op e4))のように展開される。
template < typename ... Types >
auto sum( Types && ... args )
{
return ( args + ... ) ;
}
int main()
{
// 1 + ( 2 + ( 3 + 4 ) )
sum( 1, 2, 3, 4 ) ;
}
fold式には、単項fold(unary fold)と二項fold(binary fold)が存在する。
上記の、(... op e)と(e op ...)は、単項fold式である。
二項fold式とは、(e1 op1 ... op2 e2)のことである。op1とop2は同じfold演算子でなければならず、e1とe2のどちらか片方のみが未展開のパラメーターパックでなければならない。
e2がパラメーターパックである場合、left foldになる。e1がパラメーターパックの場合、right foldになる。
template < typename ... Types >
void sum( Types && ... args )
{
// binary left fold
auto a = ( 0 + ... + args ) ;
// binary right fold
auto b = ( args + ... + 0 ) ;
// エラー、両方がパラメーターパック
auto c = ( args + ... + args ) ;
// エラー、両方が非パラメーターパック
auto d = ( 0 + ... + 0 ) ;
}
fold式に使える演算子はfold演算子である。これは以下の通り。
+ - * / % ^ & | = < > << >> += -= *= /= %= ^= &= |= <<= >>= == != <= >= && || , .* ->*
パラメーターパックが空の場合、一部のfold演算子については、以下のようにデフォルトの値が定められている。
Operator | Value when parameter pack is empty |
---|---|
* | 1 |
+ | int() |
& | -1 |
| | int() |
&& | true |
|| | false |
, | void() |
template < typename ... Empty >
void f( Empty ... e )
{// eは空のパラメーターパックとする
auto x1 = ( e * ... ) ; // 1
auto x2 = ( e + ... ) ; // int()
auto x3 = ( e & ... ) ; // -1
auto x4 = ( e | ... ) ; // int()
auto x5 = ( e && ... ) ; // true
auto x6 = ( e || ... ) ; // false
auto x7 = ( e , ... ) ; // void()
}
これ以外のfold演算子で、空のパラメーターパックをfold式でパック展開しようとすると、ill-formedになる。
N4267: u8文字リテラル
charひとつで表現できるUTF-8文字リテラル。
char A = u8'A' ; // 0x41
主な使い方として、確実にASCII文字の数値をわかりやすいリテラルでソースコードに記述できる。
N4230: 名前空間のネスト
namespace A { namespace B { namespace C {
} } }
を、
namespace A::B::C {
}
のように書ける。
N4266: 名前空間と列挙子に属性
名前空間と列挙子に属性を指定することができる。C++14までは、文法上の問題で指定できなかった。
具体的には、[[deprecated]]を指定できるようになった。
namespace [[deprecated("Use new_lib")]] lib { }
namespace new_lib { }
enum struct E { value [[deprecated("Use VALUE.")]], VALUE } ;
ドワンゴ広告
この記事はドワンゴ勤務中に書かれた。
今日は社内が盛り上がっていた。
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0