「常に5を返す関数」を持つことは、「関数を呼び出す」という意味を壊したり薄めたりするように思えます。理由があるか、この機能が必要であるか、C++ 11にはない必要があります。なぜそこにあるのですか?
// preprocessor.
#define MEANING_OF_LIFE 42
// constants:
const int MeaningOfLife = 42;
// constexpr-function:
constexpr int MeaningOfLife () { return 42; }
リテラル値を返す関数を作成し、コードレビューを行った場合、return 5を書き込む代わりに定数値を宣言する必要があると誰かが教えてくれるようです。
もう少し複雑なことをするとします。
constexpr int MeaningOfLife ( int a, int b ) { return a * b; }
const int meaningOfLife = MeaningOfLife( 6, 7 );
これで、優れた可読性を維持し、定数を評価することができ、定数を数値に設定するよりも少し複雑な処理が可能になります。
基本的には、あなたが何をしているのかがより明確になるので、保守性を高めるのに役立ちます。例としてmax( a, b )
を使用します。
template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }
とても簡単な選択ですが、定数値を使用してmax
を呼び出すと、実行時ではなくコンパイル時に明示的に計算されます。
別の良い例は、DegreesToRadians
関数です。誰もがラジアンよりも読みやすいと感じています。 180度はラジアン単位であることをご存知かもしれませんが、次のように書かれていることがより明確です。
const float oneeighty = DegreesToRadians( 180.0f );
ここにたくさんの良い情報:
constexpr
は、定数式を必要とするコンテキストで何かを評価できることを実装に伝える方法として導入されませんでした。準拠する実装は、C++ 11より前にこれを証明できました。
実装で証明できないものは、特定のコードの意図です。
constexpr
がなければ世界はどうなるでしょうか?ライブラリを開発していて、間隔(0,N]
内のすべての整数の合計を計算できるようにしたいと考えているとします。
int f (int n) {
return n > 0 ? n + f (n-1) : n;
}
コンパイラーは、渡された引数が変換中に既知である場合、上記の関数が定数式で呼び出し可能であることを簡単に証明できます。しかし、あなたはこれを意図として宣言していません-たまたまそうでした。
今、他の人がやって来て、あなたの関数を読み、コンパイラと同じ分析をします「ああ、この関数は定数式で使用できます!」で、次のコードを記述します。
T arr[f(10)]; // freakin' magic
"awesome"ライブラリ開発者として、f
が呼び出されたときに結果をキャッシュすることを決定します。同じ値のセットを何度も計算したい人はいますか?
int func (int n) {
static std::map<int, int> _cached;
if (_cached.find (n) == _cached.end ())
_cached[n] = n > 0 ? n + func (n-1) : n;
return _cached[n];
}
愚かな最適化を導入することで、constant-expressionが必要なコンテキストで発生した関数の使用をすべて中断しました。
関数がconstant-expressionで使用可能であると約束したことはありません。また、constexpr
がなければ、そのような約束を提供する方法はありません。
constexpr
が必要なのでしょうか?constexprの主な使用法は、intentを宣言することです。
エンティティがconstexpr
としてマークされていない場合、定数式で使用することを意図していません。たとえそうであっても、コンパイラーに依存してそのようなコンテキストを診断します(意図を無視しているため)。
std::numeric_limits<T>::max()
を取る:何らかの理由で、これはメソッドです。ここではconstexpr
が有益です。
別の例:別の配列と同じ大きさのC配列(またはstd::array
)を宣言したい場合。現時点でこれを行う方法は次のとおりです。
int x[10];
int y[sizeof x / sizeof x[0]];
しかし、次のように書くことができると良いでしょう。
int y[size_of(x)];
constexpr
のおかげで、次のことができます。
template <typename T, size_t N>
constexpr size_t size_of(T (&)[N]) {
return N;
}
constexpr
関数は本当にすてきで、C++に追加されました。しかし、あなたはそれが解決する問題の大部分がマクロで巧妙に回避できるという点で正しいです。
ただし、constexpr
の使用の1つには、C++ 03に相当する型付き定数がありません。
// This is bad for obvious reasons.
#define ONE 1;
// This works most of the time but isn't fully typed.
enum { TWO = 2 };
// This doesn't compile
enum { pi = 3.1415f };
// This is a file local lvalue masquerading as a global
// rvalue. It works most of the time. But May subtly break
// with static initialization order issues, eg pi = 0 for some files.
static const float pi = 3.1415f;
// This is a true constant rvalue
constexpr float pi = 3.1415f;
// Haven't you always wanted to do this?
// constexpr std::string awesome = "oh yeah!!!";
// UPDATE: sadly std::string lacks a constexpr ctor
struct A
{
static const int four = 4;
static const int five = 5;
constexpr int six = 6;
};
int main()
{
&A::four; // linker error
&A::six; // compiler error
// EXTREMELY subtle linker error
int i = Rand()? A::four: A::five;
// It not safe use static const class variables with the ternary operator!
}
//Adding this to any cpp file would fix the linker error.
//int A::four;
//int A::six;
「Going Native 2012」でのStroustrupのスピーチから:
template<int M, int K, int S> struct Unit { // a unit in the MKS system
enum { m=M, kg=K, s=S };
};
template<typename Unit> // a magnitude with a unit
struct Value {
double val; // the magnitude
explicit Value(double d) : val(d) {} // construct a Value from a double
};
using Speed = Value<Unit<1,0,-1>>; // meters/second type
using Acceleration = Value<Unit<1,0,-2>>; // meters/second/second type
using Second = Unit<0,0,1>; // unit: sec
using Second2 = Unit<0,0,2>; // unit: second*second
constexpr Value<Second> operator"" s(long double d)
// a f-p literal suffixed by ‘s’
{
return Value<Second> (d);
}
constexpr Value<Second2> operator"" s2(long double d)
// a f-p literal suffixed by ‘s2’
{
return Value<Second2> (d);
}
Speed sp1 = 100m/9.8s; // very fast for a human
Speed sp2 = 100m/9.8s2; // error (m/s2 is acceleration)
Speed sp3 = 100/9.8s; // error (speed is m/s and 100 has no unit)
Acceleration acc = sp1/0.5s; // too fast for a human
別の用途(まだ言及していません)は、constexpr
コンストラクターです。これにより、実行時に初期化する必要のないコンパイル時定数を作成できます。
const std::complex<double> meaning_of_imagination(0, 42);
それをユーザー定義のリテラルと組み合わせれば、リテラルのユーザー定義クラスを完全にサポートできます。
3.14D + 42_i;
以前は、メタプログラミングを使用したパターンがありました。
template<unsigned T>
struct Fact {
enum Enum {
VALUE = Fact<T-1>*T;
};
};
template<>
struct Fact<1u> {
enum Enum {
VALUE = 1;
};
};
// Fact<10>::VALUE is known be a compile-time constant
constexpr
は、特殊化、SFINAEなどを使用したテンプレートや奇妙なコンストラクトを必要とせずにこのようなコンストラクトを作成できるように導入されたと思いますが、実行時関数を作成するのとまったく同じですが、結果が決定されることを保証しますコンパイル時。
ただし、次のことに注意してください。
int fact(unsigned n) {
if (n==1) return 1;
return fact(n-1)*n;
}
int main() {
return fact(10);
}
これをg++ -O3
でコンパイルすると、コンパイル時にfact(10)
が実際に評価されることがわかります!
VLA対応のコンパイラー(C99モードのCコンパイラーまたはC99拡張機能を備えたC++コンパイラー)を使用すると、次のことが可能になります。
int main() {
int tab[fact(10)];
int tab2[std::max(20,30)];
}
しかし、それは現時点では非標準のC++です-constexpr
は、これに対抗する方法のように見えます(上記の場合、VLAがなくても)。また、テンプレート引数として「正式な」定数式を使用する必要があるという問題もあります。
プロジェクトをc ++ 11に切り替え始めたばかりで、constexprがまったく同じ状況に遭遇したため、同じ操作を実行する別の方法がクリーンアップされました。ここで重要な点は、constexprが宣言されている場合にのみ、配列サイズ宣言に関数を配置できることです。私が関与しているコードの領域を進めていくと、これが非常に役立つことがわかる状況がいくつかあります。
constexpr size_t GetMaxIPV4StringLength()
{
return ( sizeof( "255.255.255.255" ) );
}
void SomeIPFunction()
{
char szIPAddress[ GetMaxIPV4StringLength() ];
SomeIPGetFunction( szIPAddress );
}
他のすべての答えは素晴らしいです、私はあなたがconstexprで驚くべきことをすることができる1つのことのクールな例を挙げたいです。 See-Phit( https://github.com/rep-movsd/see-phit/blob/master/seephit.h )は、コンパイル時のHTMLパーサーおよびテンプレートエンジンです。つまり、HTMLを挿入したり、操作可能なツリーを取得したりできます。コンパイル時に解析を行うと、パフォーマンスが少し向上します。
Githubページの例から:
#include <iostream>
#include "seephit.h"
using namespace std;
int main()
{
constexpr auto parser =
R"*(
<span >
<p color="red" height='10' >{{name}} is a {{profession}} in {{city}}</p >
</span>
)*"_html;
spt::tree spt_tree(parser);
spt::template_dict dct;
dct["name"] = "Mary";
dct["profession"] = "doctor";
dct["city"] = "London";
spt_tree.root.render(cerr, dct);
cerr << endl;
dct["city"] = "New York";
dct["name"] = "John";
dct["profession"] = "janitor";
spt_tree.root.render(cerr, dct);
cerr << endl;
}
あなたの基本的な例は、定数自体と同じ議論をしています。使用する理由
static const int x = 5;
int arr[x];
以上
int arr[5];
ずっとメンテナンスしやすいからです。 constexprを使用すると、既存のメタプログラミング手法よりもはるかに高速に書き込みおよび読み取りができます。
constexpr
を使用する場合:
いくつかの新しい最適化が可能になります。 const
は伝統的に型システムのヒントであり、最適化に使用することはできません(たとえば、const
メンバー関数はconst_cast
を使用して、とにかくオブジェクトを合法的に変更できるため、const
は最適化に対して信頼できません)。
constexpr
は、関数への入力がconstである場合、式実際が定数であることを意味します。考慮してください:
class MyInterface {
public:
int GetNumber() const = 0;
};
これが他のモジュールで公開されている場合、コンパイラは、GetNumber()
が呼び出されるたびに異なる値を返さないことを信頼できません-間に非const呼び出しがなくても連続して-実装でconst
がキャストされる可能性があるためです(明らかに、これを行ったプログラマーは撃たれるべきですが、言語はそれを許可します。したがって、コンパイラーは規則を守らなければなりません。)
constexpr
の追加:
class MyInterface {
public:
constexpr int GetNumber() const = 0;
};
コンパイラは、constexpr
が戻り値が変わらないことをより強力に保証するため、GetNumber()
の戻り値がキャッシュされる最適化を適用し、GetNumber()
への追加の呼び出しを排除できます。