残念ながら、私はconstexpr
、ヘッダーファイルで宣言されたグローバル定数、およびodrについて多少混乱しています。
要するに:ここから結論を出すことができますか
https://isocpp.org/files/papers/n4147.pdf
それ
constexpr MyClass const MyClassObj () { return MyClass {}; }
constexpr char const * Hello () { return "Hello"; }
よりも好ましい
constexpr MyClass const kMyClassObj = MyClass {};
constexpr char const * kHello = "Hello";
それらのglobally宣言/定義されたエンティティを「使用」したいが、どのように私はそれらを使用しますか?
注:C++ 17以降、 変数をインラインとして と宣言できます。
TL; DR:(非常に)安全な側になりたい場合は、constexpr関数を使用してください。ただし、本質的に必要なわけではなく、これらのオブジェクトに対して簡単な操作を実行していて、それらの値のみに関心がある場合、または以下に示す危険なシナリオでそれらを使用しない場合は、確かに必要ありません。
基本的な問題は、あなたのような名前空間スコープのconst
変数が(一般的に)内部リンケージ( [basic.link] /(3.2) )を持っていることです。これは、対応するヘッダーをコンパイルする各変換ユニットが異なるエンティティ(つまりシンボル)を監視することを意味します。
ここで、これらのオブジェクトを使用するヘッダーにテンプレートまたはインライン関数があると想像してください。 ODRは、このシナリオについて非常に正確です [basic.def.odr]/6 :
"定数式で初期化"は、constexpr
について話しているので、確かに満たされます。 "オブジェクトはD
のすべての定義で同じ値を持っています"あなたが気にしないのであれば。
「オブジェクトはodrで使用されていません」がおそらく唯一の疑わしい条件です。基本的に、変数の実行時の存在をシンボルとして必要としない必要があります。これは、次のことを意味します。
参照にバインドしません(=>転送しません!)
あなたは(明示的にも暗黙的にも)そのアドレスを取得しません。
2番目のルールの唯一の例外は配列です。配列は、生成されたglvalueについて上記の2つのルールに違反しない限り、添え字操作内で暗黙的にアドレスを取ることができます。
より正確には、odr-useは [basic.def.odr]/ :によって管理されます。
名前が潜在的に評価される式
x
として表示される変数ex
は、ex
によってodr-使用されます。ただし、左辺値から右辺値への変換(4.1)をx
に適用すると、重要な関数を呼び出さない定数式(5.20)が生成されます。 、x
がオブジェクトの場合、ex
は式e
の潜在的な結果のセットの要素であり、左辺値から右辺値への変換(4.1)がe
に適用されるか、e
が破棄された値の式(第5節)です。 )。
L-t-rを任意のconstexpr
変数に適用すると、最初の部分で必要とされるように動作します。 2番目の部分では、変数を実際のオブジェクトではなく値として使用する必要があります。つまり、最終的には破棄されるか、直接評価され、上記の経験則が得られます。
インライン関数やテンプレートなどの内部での変数のodr-useを回避する場合は、問題ありません。ただし、対応するconstexpr関数の戻り値を使用する場合、prvaluesはすでに値/リテラル(オブジェクトではない)のように動作しており、constexpr関数はインラインであり、間違いなくODRに違反しないため、心配する必要はありません(ifその中でconstexpr
変数を使用しません!)。