web-dev-qa-db-ja.com

ローカル変数のアドレスはconstexprですか?

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の例に他に何かありますか?

23
johnnyodonnell

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の値を判別できます。それはうまくコンパイルします。

17
doug

「C++プログラミング言語(第4版)」の私のハードコピーに提供されているセクション10.4.5の例は正しくないようです。そして、ローカル変数のアドレスはconstexprではないと結論付けました。

この例は、次に示すように一部のpdfバージョンで更新されているようです。

enter image description here

10
johnnyodonnell

この回答は、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として修飾できません。

3
眠りネロク

間違いを指摘した他の回答に追加するために、C++標準では、static-storage durationのオブジェクトへのconstexprポインターのみを許可し、など、またはnullptr[expr.const/8] を具体的に参照してください#8.2;

それは注目に値します:

  • string-literalshavestatic-storage duration
  • 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;
}
1
WhiZTiM