C++14では、constexprの制限が大幅に緩和された。
C++11で追加されたconstexpr関数は、コンパイル時定数式として評価できる関数である。
constexpr std::size_t size()
{ return 10 ; }
// OK、size()は定数式
int a[size()] ;
しかし、C++11のconstexpr関数には、極めて厳しい制約がある。constexpr関数の本体は、実質、return文ひとつしか認められていないのだ。
プログラミングというのは、通常、変数や条件分岐やループといった機能を使う。constexpr関数では、それらの機能は実現可能ではあるが、C++としては極めて不自然な方法を用いなければならない。変数は引数に追い出し、条件分岐は条件演算子(?:)を使い、ループには再帰をしなければならない。
たとえば、変数、条件分岐、ループを使いたいsqrtを、C++11のconstexpr関数で書くと、以下のようになる。
// C++11のconstexpr関数によるsqrtの実装
template < typename T >
constexpr T sqrt_aux( T s, T x, T prev )
{
return x != prev ?
sqrt_aux( s, ( x + s / x ) / 2.0, x ) : x ;
}
template < typename T >
constexpr T sqrt( T s )
{ return sqrt_aux( s, s/2.0, s ) ; }
C++14では、constexpr関数の制約が大幅に緩和された。C++14のconstexpr関数では、sqrtは以下のように書くことができる。
template < typename T >
constexpr T sqrt( T s )
{
T x = s / 2.0 ;
T prev = 0.0 ;
while ( x != prev )
{
prev = x ;
x = (x + s / x ) / 2.0 ;
}
return x ;
}
C++14のconstexpr関数は、変数の宣言ができる。ただし、必ず初期化されなければならず、staticとthread_local変数は宣言できない。
constexpr int f( int x )
{
int l = x ; // OK
int e1 ; // エラー、未初期化
static int e2 ; // エラー
thread_local int e3 ; // エラー
return 0 ;
}
変数を変更することもできる。変更できる変数は、定数式の評価とともに寿命が始まったものだけだ。
constexpr int f( int x )
{
int l = x ;
l += 1 ;
++l ;
--l ;
return l ;
}
条件分岐として、if文とswitch文を使うことができる。goto文は使えない。
ループとして、for, while, do whileを使うことができる。
まとめとして、一言で言えば、C++14のconstexpr関数は、普通に書けるようになったということだ。
なおこの機能はClang 3.4で実装されている。GCCではまだ実装されていない。
Clang - C++1z, C++14, C++11 and C++98 Status
C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)
See Also:
本の虫: C++14の新機能: decltype(auto)
本の虫: C++14の新機能: 初期化lambdaキャプチャー
ドワンゴ広告
この記事はドワンゴ勤務中に書かれた。
そういえば、最近、社内で昼ボドゲをしていない。金曜の夜は大抵ボドゲが行われているが。
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
あぁ、constexpr!VCが夢見る機能デス。
ReplyDelete