2014-12-12

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演算子については、以下のようにデフォルトの値が定められている。

Table N. Value of folding empty sequences
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

No comments: