33

次のプログラムがコンパイルされます。

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage

int main()
{
    Test<&var> test;
}

しかし、これはそうではありません。これは私にとって驚きです。

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
    Test<ptr> test; //FAIL! Expected constant expression.
}

別の例:

int main()
{
   const int size = 42;
   int ok[*&size]; //OK

   const int * const pSize = &size;
   int fail[*pSize]; //FAIL
}

ポインタが定数であり、定数式で初期化されているかどうかに関係なく、ポインタを定数式にすることはできないと結論付けました。

質問:

  1. 私の結論は本当ですか?
  2. もしそうなら、なぜポインタは定数式になれないのですか?そうでない場合は、なぜ上記のプログラムをコンパイルしないのですか?
  3. C ++ 0x(もしあなたがそうするならC ++ 11)は何かを変えますか?

洞察をありがとう!

4

3 に答える 3

14

もう少し複雑です。C++03およびC++11では、がローカル静的/クラス静的または名前空間スコープ変数の&var場合、は定数式です。varこれをアドレス定数式と呼びます。その定数式を使用したクラス静的または名前空間スコープポインター変数の初期化は、定数式であるため、コードが実行される前(静的初期化フェーズ)に実行されることが保証されます。

ただし、アドレスを格納するconstexprポインター変数である&varC ++ 11以降のみがアドレス定数式として使用でき、C ++ 11以降のみ、アドレス定数式を逆参照できます(実際には、さらに多くの逆参照が可能です。ローカル配列要素のアドレスですが、トピックはそのままにしておきます)、間接参照の前に初期化された定数整数変数またはconstexpr変数を参照している場合は、定数式が再び得られます(型と値のカテゴリに応じて、定数式の種類)異なる場合があります)。そのため、以下は有効なC++11です。

int const x = 42;
constexpr int const *px = &x;

// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }

もしそうなら、なぜポインタは定数式になれないのですか?そうでない場合は、なぜ上記のプログラムをコンパイルしないのですか?

これは、標準の表現における既知の制限です。現在& object、ポインタタイプのテンプレートパラメータに対して、引数または、として他のテンプレートパラメータのみを許可しています。コンパイラはもっと多くのことができるはずですが。

于 2011-09-12T20:33:45.983 に答える
2

C++0xではまだ許可されていません。 temp.arg.nontype必要:

非型、非テンプレートのテンプレートパラメータのテンプレート引数は、次のいずれかでなければなりません。

  • 整数型または列挙型の非型テンプレートパラメーターの場合、テンプレートパラメーターの型の変換された定数式(5.19)。また
  • 非型テンプレートパラメータの名前。また
  • 静的ストレージ期間と外部または内部リンケージを持つオブジェクト、または関数テンプレートと関数テンプレートIDを含み、非静的クラスメンバーを除く外部または内部リンケージを持つ関数のアドレスを指定する定数式(5.19 )括弧)as & id-expression、ただし、名前が関数または配列を参照している場合は&を省略でき、対応するtemplate-parameterが参照の場合は省略できます。また
  • nullポインタ値(4.10)に評価される定数式。また
  • nullメンバーポインタ値(4.11)に評価される定数式。また
  • 5.3.1で説明されているように表現されたメンバーへのポインタ。

元の答え:

  1. C ++ 03では、整数式のみが定数式になります。
  2. 標準がそう言っているので(当然)。
  3. C ++ 0xでは、n3290にはconstexprポインターで使用する例が含まれています。constexprしたがって、トップレベルの代わりにキーワードを使用する必要がありますが、これで、試行していることが可能になるはずですconst

gccのバグも含まれており、g++は標準ドラフト独自の有効なconstexpr使用例を拒否します。

于 2011-09-12T18:25:20.310 に答える
1

問題は、C ++プログラムはメモリ内の任意の場所にロードできるためvar、プログラムを実行するたびにグローバルのアドレスが異なる可能性があるためです。プログラムを2回実行するとどうなりますか? var明らかに2つの異なる場所にあります。

さらに悪いことに、あなたの例では、スタック上の変数のアドレスを取得します!これを見てください:

void myfunction( unsigned int depth) {
     const int myvar = depth;
     const int * const myptr = &myvar;
     if (depth)
         myfunction(depth-1);
}

mainがmyfunction(3)を呼び出すと、3つのmyvarが別々の場所に作成されます。コンパイル時に、作成されたmyvarの数、ましてや正確な場所を知る方法はありません。

最後に、変数を宣言することconstは、「約束する」という意味であり、コンパイル時定数であることを意味するものではありません。この例を参照してください。

int main(int argc, char** argv) {
    const int cargc = argc;
    char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}
于 2011-09-12T18:51:56.370 に答える