次のような整数型のコンパイル時定数を定義する場合(関数およびクラススコープで)、どの構文が最適ですか?
static const int kMagic = 64; // (1)
constexpr int kMagic = 64; // (2)
(1)
はC++ 98/03コンパイラでも機能しますが、代わりに(2)
は少なくともC++ 11を必要とします。 2つの間に他の違いはありますか?現代のC++コードではどちらか一方を優先すべきですか?その理由は?
[〜#〜] edit [〜#〜]
Godbolt's CE でこのサンプルコードを試しました。
int main()
{
#define USE_STATIC_CONST
#ifdef USE_STATIC_CONST
static const int kOk = 0;
static const int kError = 1;
#else
constexpr int kOk = 0;
constexpr int kError = 1;
#endif
return kOk;
}
static const
の場合、これはGCC 6.2によって生成されたアセンブリです。
main::kOk:
.zero 4
main::kError:
.long 1
main:
Push rbp
mov rbp, rsp
mov eax, 0
pop rbp
ret
一方、constexpr
の場合:
main:
Push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 0
mov DWORD PTR [rbp-8], 1
mov eax, 0
pop rbp
ret
どちらの場合も-O3
で同じ(最適化された)アセンブリを取得しますが:
main:
xor eax, eax
ret
編集#2
私はこの簡単なコードを試しました (イデオンでのライブ) :
#include <iostream>
using namespace std;
int main() {
const int k1 = 10;
constexpr int k2 = 2*k1;
cout << k2 << '\n';
return 0;
}
const int k1
は、constexpr int k2
の計算に使用されるため、compile-timeで評価されることを示しています。
ただし、double
sにはdifferent動作があるようです。そのための別の質問を作成しました here 。
のコンパイル時定数の宣言について話している限り スカラー 整数型または列挙型の場合、const
(クラススコープのstatic const
)とconstexpr
を使用してもまったく違いはありません。
コンパイラは、定数式でstatic const int
オブジェクト(定数初期化子で宣言された)をサポートする必要があることに注意してください。つまり、コンパイラは、そのようなオブジェクトをコンパイル時定数として扱う以外に選択肢がありません。さらに、そのようなオブジェクトがODRで使用されていない限り、定義は不要です。これは、ランタイム値として使用されないことをさらに示しています。
また、定数初期化のルールは、ローカルstatic const int
オブジェクトが動的に初期化されるのを防ぎます。つまり、そのようなオブジェクトをローカルで宣言することによるパフォーマンスの低下はありません。さらに、静的な初期化の順序付け問題に対する積分static
オブジェクトの耐性は、言語の非常に重要な機能です。
constexpr
は、C++で最初にconst
を介して定数初期化子で実装された概念の拡張および一般化です。整数型の場合、constexpr
はconst
がすでに行ったことよりも特別なものを提供しません。 constexpr
は、初期化子の「安定性」の早期チェックを実行するだけです。ただし、constexpr
はその目的のために特別に設計された機能であるため、スタイルに適していると言うかもしれません。
constexpr
変数は、コンパイル時に使用可能な値を持つことが保証されています。一方、static const
メンバーまたはconst
変数は、コンパイル時の値またはランタイム値のいずれかを意味します。 constexpr
と入力すると、コンパイル時の値の意図をconst
よりもはるかに明示的に表現できます。
もう1つ、C++ 17では、constexpr
静的データメンバー変数もインラインになります。つまり、static constexpr
変数の行外定義は省略できますが、static const
変数は省略できません。
コメントセクションでの要求として、関数スコープのstatic const
に関する詳細な説明を以下に示します。
関数スコープのstatic const
変数はほとんど同じですが、自動保存期間の代わりに、静的保存期間があります。つまり、何らかの方法で変数をグローバルとして宣言することと同等ですが、関数内でのみアクセス可能です。
static
変数は関数の最初の呼び出しで初期化されるのは事実ですが、const
でもあるため、コンパイラは値をインライン化し、変数を完全に最適化しようとします。したがって、関数では、ifこの特定の変数の値はコンパイル時に既知であり、コンパイラーはほとんどの場合それを最適化します。
ただし、関数スコープのstatic const
の値がコンパイル時に不明な場合、silently関数を作成します(非常に値を初期化する必要があるため、低速です実行時関数が最初に呼び出されたとき。さらに、関数が呼び出されるたびに値が初期化されるかどうかを確認する必要があります。
これがconstexpr
変数の利点です。コンパイル時に値がわからない場合は、遅い関数ではなくコンパイルエラーです。コンパイル時に変数の値を決定する方法がない場合は、コンパイラーがそのことを通知し、それについて何かを行うことができます。