本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

最近のC++17事情

C++1z、あるいはC++17とも呼ばれている次のC++規格の、最近の事情はどうなっているのか。すでにドラフトに取り入れられた機能もあるので、現在の最新の状況を見ていこう。もうすでに紹介したものも含まれているが、おさらいとしてみていく。また、ここで解説する新機能は、いずれもすでにドラフト入りしているが、正式に規格制定される際に変わる可能性がある。

N3928: メッセージなしstatic_assert

C++11で入ったstatic_assertは、必ず文字列リテラルを書かなければならなかった。

static_assert( INT_MAX >= 2147483647, "This code assumes at least 32-bit int." ) ;
static_assert( true == true, "You're compiler is fundamentally wrong." ) ;

しかし、この文字列リテラルの使われ方は規定されていない。特にメッセージを書きたくなくても、文字列リテラルは、文法上必ず書かなくてはならない。


static_cast( std::numeric_limits<double>::is_iec559, "" ) ;

C++1zでは、文字列リテラルを書かなくてもよくなる。


static_cast( false ) ;

N4086: Removing trigraphs??!

トライグラフが取り除かれる。

// C++14までは#
// C++1zでは??=
std::cout << "??=" ;

N4051: Allow typename in a template template parameter

テンプレートテンプレートパラメーターにtypenameキーワードが使えるようになる。

template <
    template < typename T >
    class U
>
struct X ;

が、

template <
    template < typename T >
    typename U
>
struct X ;

とも書けるようになる。

N3922: New Rules for auto deduction from braced-init-list.

auto指定子で直接初期化でリスト初期化を書いた場合の挙動を変更する。C++14で合法なコードが違法になる珍しいケースだ。


auto a{ 1, 2, 3 } ;

このコードは、C++14までは合法なコードであり、decltype(a)は、std::initializer_list<int>となる。

C++1zでは、このコードは違法である。auto指定子で直接初期化でリスト初期化の場合は、単一のinitializer-clauseしか書くことができなくなった。

// aの型はint
auto a{ 1 } ;

N4295: Fold expressions

今回紹介する新機能の中で、一番面白い機能がこれだ。パラメーターパックに対するfold式がC++に入る。

パラメーターパック全てに対して演算子を適用したい場面がある。

// 与えた実引数にoperator +を適用して合計値を返す関数テンプレートsum
sum( 1, 2, 3 ) ; // 6
sum( 1, 2, 3, 4 ) ; // 10

このようなsumをC++14で書くと以下のようになる。

template < typename T >
T sum( T && t )
{
    return t ;
}

template < typename T, typename ... Args >
T sum( T && t, Args && ... args )
{
    return t + sum( std::forward<Args>(args)... ) ;
}

やりたいことは、パラメーターパックのそれぞれの実引数にoperator +を適用したいだけなのに、やたらと面倒なコードだ。

そこで、C++1zには、パラメーターパックに対するfold式が入る。

template < typename ... Args >
auto sum( Args && ... args )
{
    return ( args + ... ) ;
}

これは、sum( 1, 2, 3, )に対して、 (((1 + 2) + 3) + 4)のようにleft foldされる。

逆に以下のように書けば、

template < typename ... Args >
auto sum( Args && ... args )
{
    return ( ... + args ) ;
}

(1 + ( 2 + ( 3 + 4 ) ) )のように、right foldされる。

fold式は、必ず括弧で囲まなければならない。


( pack + ... ) ; // OK
pack + ... ; // エラー、括弧で囲まれていない

fold式には、unary(単項) foldとbinary(二項) foldがある。binary foldは、(e1 op1 ... op2 e2)という形を取る。op1とop2は同じfold演算子でなければならず、e1とe2は、どちらか片方だけがパラメーターパックでなければならない。e2がパラメーターパックであった場合はleft fold、e1がパラメーターパックであった場合はright foldとなる。


template < typename ... Types >
void f( Types ... args )
{
    ( 1 + ... + args ) ; // binary left fold
    ( args + ... + 1 ) ; // binary right fold
}

それぞれ、(( ( 1 + args0 ) + args1) + ... + argsN )と、args0 + ( args1 + ( ... argsN + 1) )のようにパック展開される。

N4267: Adding u8 character literals

UTF-8文字リテラルの追加。プレフィクスu8の文字リテラルで、UTF-8のコード単位一つで表現可能な文字が記述できる。


char a = u8'a' ; // OK
char b = u8'あ' ; // エラー、UTF-8で符号化するにはコード単位が3個必要。

N4230: Nested namespace definition (revision 2)

名前空間の宣言をネストできるようになる。

namepsace A {
    namespace B {
        namespace C {
            // ...
        }
    }
}

のようなコードが、

namespace A::B::C {
// ...
}

のように書ける。

N4266: Attributes for namespaces and enumerators

名前空間とenumeratorにattributeが記述できるようになる。C++14までは、文法上の制約で記述することができなかった。これにより、名前空間やenumeratorにdeprecatedが指定できる。記述する場所は、名前空間ならnamespaceキーワードの前、識別子の後、enumeratorならば、識別子の後だ。


namespace [[deprecated("This namespace is deprecated.")]] libv1 { }

enum class E { value [[deprecated("This enumerator is deprecated.")]] = 0 ; }

N4268: Allow constant evaluation for all non-type template arguments

すべての非型テンプレート実引数でコンパイル時評価を可能にするように制限を緩和する。

以下のコードは違法である。

template<int *p> struct A {};
int n;
A<&n> a; // ok

constexpr int *p() { return &n; }
A<p()> b; // エラー

理由は、定数として渡せるポインターはnullだけだからである。

constexpr int *p() { return nullptr ; }
A<p()> b; // OK

constexprがある今、この制約は時代にそぐわない。そこで、非型テンプレート実引数に、任意の定数式を渡せるように制限を緩和された。つまり、上のコードは違法ではなくなる。

ドワンゴ広告

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

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0