Bjarne Stroustrupの本「C++プログラミング言語(第4版)」のp。 267(セクション10.4.5アドレス定数式)では、ローカル変数のアドレスがconstexpr
変数に設定されているコード例を使用しています。これは奇妙に見えると思ったので、g ++バージョン7.3.0でサンプルを実行してみましたが、同じ結果が得られませんでした。以下は彼のコード例です(少し省略していますが):
extern char glob;
void f(char loc) {
constexpr const char* p0 = &glob; // OK: &glob's is a constant
constexpr const char* p2 = &loc; // OK: &loc is constant in its scope
}
これを実行すると、次のようになります。
error: ‘(const char*)(& loc)’ is not a constant expression
私が知らないg ++で何かが起こっているのですか、それともBjarneの例に他に何かありますか?
Bjarne Stroustrupの著書「C++プログラミング言語(第4版)」のp。 267には、OPの質問で概説されているエラーがあります。現在の印刷と電子コピーは「修正」されていますが、後で説明する別のエラーが発生しました。現在は次のコードを参照しています。
constexpr const char* p1="asdf";
「asdf」は固定メモリロケーションに格納されているため、これで問題ありません。以前の印刷では、本はここで誤ります:
void f(char loc) {
constexpr const char* p0 = &glob; // OK: &glob's is a constant
constexpr const char* p2 = &loc; // OK: &loc is constant in its scope
}
ただし、loc
は固定メモリ位置にありません。それはスタック上にあり、いつ呼び出されるかによって場所が異なります。
ただし、現在の第4版の印刷には別のエラーがあります。これは10.5.4のコードをそのまま使用したものです。
int main() {
constexpr const char* p1 = "asdf";
constexpr const char* p2 = p1; // OK
constexpr const char* p3 = p1+2; // error: the compiler does not know the value of p1
}
これは間違っています。コンパイラー/リンカーはp1の値を知っており、リンク時にp1+2
の値を判別できます。それはうまくコンパイルします。
この回答は、x86-64アーキテクチャの例を分析して、ローカル変数のアドレスをconstexpr
にできない理由を明らかにしようとしています。
ローカル変数_local_var
_のアドレスを表示し、自分自身を再帰的にn
回呼び出す、次のおもちゃの関数print_addr()
について考えてみます。
_void print_addr(int n) {
int local_var{};
std::cout << n << " " << &local_var << '\n';
if (!n)
return; // base case
print_addr(n-1); // recursive case
}
_
print_addr(2)
を呼び出すと、x86-64システムで次の出力が生成されました。
_2 0x7ffd89e2cd8c
1 0x7ffd89e2cd5c
0 0x7ffd89e2cd2c
_
ご覧のとおり、_local_var
_の対応するアドレスは、print_addr()
の呼び出しごとに異なります。また、関数呼び出しが深いほど、ローカル変数_local_var
_のアドレスが低くなっていることがわかります。これは、x86-64プラットフォームでスタックが下向きに(つまり、上位から下位へ)成長するためです。
上記の出力の場合、 call stack はx86-64プラットフォームでは次のようになります。
_ | . . . |
Highest address ----------------- <-- call to print_addr(2)
| print_addr(2) |
-----------------
| print_addr(1) |
-----------------
| print_addr(0) | <-- base case, end of recursion
Lowest address ----------------- Top of the stack
_
上の各長方形は、print_addr()
を呼び出すたびに スタックフレーム を表します。各呼び出しの_local_var
_は、対応するスタックフレームにあります。 print_addr()
への各呼び出しの_local_var
_は独自の(異なる)スタックフレームにあるため、_local_var
_のアドレスは異なります。
結論として、関数内のローカル変数のアドレスは、関数へのすべての呼び出しで同じではない可能性があるため(つまり、各呼び出しのスタックフレームがメモリ内の異なる位置にある可能性があります)、そのような変数のアドレスはコンパイル時に決定されるため、constexpr
として修飾できません。
間違いを指摘した他の回答に追加するために、C++標準では、static-storage durationのオブジェクトへのconstexprポインターのみを許可し、など、またはnullptr
。 [expr.const/8] を具体的に参照してください#8.2;
それは注目に値します:
extern
変数の宣言における制約に基づいて、それらは本質的にstatic-storage durationまたはを持ちますスレッドローカルストレージ期間。したがって、これは有効です。
#include <string>
extern char glob;
std::string boom = "Haha";
void f(char loc) {
constexpr const char* p1 = &glob;
constexpr std::string* p2 = nullptr;
constexpr std::string* p3 = &boom;
}