著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou
アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる
筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。
C++1z、あるいはC++17とも呼ばれている次のC++規格の、最近の事情はどうなっているのか。すでにドラフトに取り入れられた機能もあるので、現在の最新の状況を見ていこう。もうすでに紹介したものも含まれているが、おさらいとしてみていく。また、ここで解説する新機能は、いずれもすでにドラフト入りしているが、正式に規格制定される際に変わる可能性がある。
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 ) ;
トライグラフが取り除かれる。
// C++14までは#
// C++1zでは??=
std::cout << "??=" ;
テンプレートテンプレートパラメーターにtypenameキーワードが使えるようになる。
template <
template < typename T >
class U
>
struct X ;
が、
template <
template < typename T >
typename U
>
struct X ;
とも書けるようになる。
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 } ;
今回紹介する新機能の中で、一番面白い機能がこれだ。パラメーターパックに対する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) )のようにパック展開される。
UTF-8文字リテラルの追加。プレフィクスu8の文字リテラルで、UTF-8のコード単位一つで表現可能な文字が記述できる。
char a = u8'a' ; // OK
char b = u8'あ' ; // エラー、UTF-8で符号化するにはコード単位が3個必要。
名前空間の宣言をネストできるようになる。
namepsace A {
namespace B {
namespace C {
// ...
}
}
}
のようなコードが、
namespace A::B::C {
// ...
}
のように書ける。
名前空間と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 ; }
すべての非型テンプレート実引数でコンパイル時評価を可能にするように制限を緩和する。
以下のコードは違法である。
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