回答:
簡単に言えば、static
便利であるだけでなく、常に望ましいことです。
まず、static
およびconstexpr
は互いに完全に独立していることに注意してください。static
実行中のオブジェクトの寿命を定義します。constexpr
コンパイル時にオブジェクトを使用できるように指定します。コンパイルと実行は、時間的にも空間的にもばらばらで不連続です。したがって、プログラムがコンパイルされると、constexpr
関連性はなくなります。
宣言されたすべての変数constexpr
は暗黙的ですconst
がconst
、static
ほぼ直交しています(static const
整数との相互作用を除く)。
C++
オブジェクトモデル(§1.9)は、すべてのビットフィールドは、メモリの少なくとも1つのバイトを占有し、アドレスを持っている以外のオブジェクトことを必要とします。さらに、特定の瞬間にプログラムで観察可能なすべてのそのようなオブジェクトは、別個のアドレスを持つ必要があります(パラグラフ6)。これは、ローカルの非静的const配列を使用して関数を呼び出すたびに、スタックに新しい配列を作成することをコンパイラーに要求するものではありません。これはas-if
、そのようなオブジェクトが他にないことを証明できれば、コンパイラーは原則を逃れる可能性があるためです。観察された。
残念ながら、関数が自明でない限り(たとえば、本体が変換単位内に表示されない他の関数を呼び出さない)、配列は定義によりアドレスになっているため、これを証明するのは簡単ではありません。そのため、ほとんどの場合、非静的const(expr)
配列は呼び出しのたびにスタック上に再作成する必要があり、コンパイル時にそれを計算できるという点がなくなります。
一方、ローカルstatic const
オブジェクトはすべてのオブザーバーによって共有され、さらに、それが定義されている関数が呼び出されない場合でも初期化されることがあります。したがって、上記のいずれも当てはまりません。コンパイラーは、そのインスタンスを1つだけ生成するだけでなく、単一のインスタンスを読み取り専用ストレージに自由に生成できます。
だからあなたは間違いなくstatic constexpr
あなたの例で使うべきです。
ただし、を使用したくない場合がありますstatic constexpr
。ない限り、constexpr
宣言されたオブジェクトは、どちらかであるODR-使用または宣言はstatic
、コンパイラがすべてでそれを含まないように自由です。コンパイルconstexpr
されたプログラムを不要なバイトで汚染することなく、コンパイル時の一時配列を使用できるため、これは非常に便利です。その場合、は実行時にオブジェクトを強制的に存在させる可能性があるためstatic
、を使用したくないことは明らかですstatic
。
static
と言っていますconstexpr
が、それらは直交していて独立しており、異なることをしています。次に、ODRの使用を無視するため、この2つを組み合わせない理由を述べます(これは便利なようです)。静的は実行時のものなので、なぜconstexprで静的を使用する必要があるのかはまだわかりません。constexprでstaticが重要である理由を説明したことはありません。
static constexpr
(定数配列をすべての関数呼び出しで再作成する必要がないようにすること)の重要性を説明したと思いましたが、わかりやすくするためにいくつかの単語を微調整しました。ありがとう。
constexpr
定数変数がコンパイル時のコンテキストでのみ使用され、実行時に必要とされない場合static
は、意味がありません。実行時に到達した時点で、値は実質的に「インライン化」されているためです。ただし、constexpr
がランタイムコンテキストで使用される場合(つまり、constexpr
がconst
暗黙的に変換され、ランタイムコードの物理アドレスで使用可能である必要がある場合)static
、ODRコンプライアンスなどを確保する必要があります。少なくとも、私の理解です。
static constexpr int foo = 100;
。コードがのようなものでない限り、コンパイラーがfoo
リテラルの代わりにあらゆる場所の使用法を代用できなかった理由はありません。だから、上から、この場合には有用性を持っていない、実行時には存在しません。繰り返しになりますが、コンパイラ次第です。100
&foo
static
foo
foo
与えられた答えに加えて、そのコンパイラに注意することの価値を初期化するために必要とされていないconstexpr
との差異ことを知って、コンパイル時に変数constexpr
とは、static constexpr
それが使用することですstatic constexpr
あなたは変数が一度だけ初期化されていることを確認します。
次のコードは、constexpr
変数が(同じ値でも)複数回初期化される方法を示していますが、static constexpr
確実に1回だけ初期化されています。
さらに、コードはconstexpr
、const
と組み合わせた場合のに対する利点を比較しstatic
ます。
#include <iostream>
#include <string>
#include <cassert>
#include <sstream>
const short const_short = 0;
constexpr short constexpr_short = 0;
// print only last 3 address value numbers
const short addr_offset = 3;
// This function will print name, value and address for given parameter
void print_properties(std::string ref_name, const short* param, short offset)
{
// determine initial size of strings
std::string title = "value \\ address of ";
const size_t ref_size = ref_name.size();
const size_t title_size = title.size();
assert(title_size > ref_size);
// create title (resize)
title.append(ref_name);
title.append(" is ");
title.append(title_size - ref_size, ' ');
// extract last 'offset' values from address
std::stringstream addr;
addr << param;
const std::string addr_str = addr.str();
const size_t addr_size = addr_str.size();
assert(addr_size - offset > 0);
// print title / ref value / address at offset
std::cout << title << *param << " " << addr_str.substr(addr_size - offset) << std::endl;
}
// here we test initialization of const variable (runtime)
void const_value(const short counter)
{
static short temp = const_short;
const short const_var = ++temp;
print_properties("const", &const_var, addr_offset);
if (counter)
const_value(counter - 1);
}
// here we test initialization of static variable (runtime)
void static_value(const short counter)
{
static short temp = const_short;
static short static_var = ++temp;
print_properties("static", &static_var, addr_offset);
if (counter)
static_value(counter - 1);
}
// here we test initialization of static const variable (runtime)
void static_const_value(const short counter)
{
static short temp = const_short;
static const short static_var = ++temp;
print_properties("static const", &static_var, addr_offset);
if (counter)
static_const_value(counter - 1);
}
// here we test initialization of constexpr variable (compile time)
void constexpr_value(const short counter)
{
constexpr short constexpr_var = constexpr_short;
print_properties("constexpr", &constexpr_var, addr_offset);
if (counter)
constexpr_value(counter - 1);
}
// here we test initialization of static constexpr variable (compile time)
void static_constexpr_value(const short counter)
{
static constexpr short static_constexpr_var = constexpr_short;
print_properties("static constexpr", &static_constexpr_var, addr_offset);
if (counter)
static_constexpr_value(counter - 1);
}
// final test call this method from main()
void test_static_const()
{
constexpr short counter = 2;
const_value(counter);
std::cout << std::endl;
static_value(counter);
std::cout << std::endl;
static_const_value(counter);
std::cout << std::endl;
constexpr_value(counter);
std::cout << std::endl;
static_constexpr_value(counter);
std::cout << std::endl;
}
可能なプログラム出力:
value \ address of const is 1 564
value \ address of const is 2 3D4
value \ address of const is 3 244
value \ address of static is 1 C58
value \ address of static is 1 C58
value \ address of static is 1 C58
value \ address of static const is 1 C64
value \ address of static const is 1 C64
value \ address of static const is 1 C64
value \ address of constexpr is 0 564
value \ address of constexpr is 0 3D4
value \ address of constexpr is 0 244
value \ address of static constexpr is 0 EA0
value \ address of static constexpr is 0 EA0
value \ address of static constexpr is 0 EA0
ご覧のとおり、自分constexpr
は複数回static
初期化されています(アドレスは同じではありません)一方で、キーワードは初期化が1回だけ実行されることを保証します。
constexpr const short constexpr_short
場合にエラーを表示するために使用できないのか
constexpr const
意味がありません。constexpr
すでにあるため、コンパイラーは1回または複数回のconst
追加をconst
無視します。エラーをキャッチしようとしていますが、これはエラーではありません。これがほとんどのコンパイラの動作方法です。
const
から離れてキャストすることはできません。を指すconst
からのみキャストできます。しかし、それは重要ではありません。ポイントは、自動オブジェクトが静的アドレスを持つことができないことです。私は、言ったようにコンパイルが終了すると有意義でなくなる(オブジェクトがさえ、実行時に存在することが保証されていないので、すべての恐らく何も、。)、そう離れてキャストには何もありませんconst X*
X
constexpr