5

MinGW GNU コンパイラを使用して C++ を作成していますが、外部で定義された整数変数を switch ステートメントのケースとして使用しようとすると、問題が発生します。次のコンパイラ エラーが発生します

整数変数を extern として定義したので、コンパイルする必要があると思いますが、何が問題なのか知っている人はいますか?

以下に例を示します。

test.cpp

#include <iostream>
#include "x_def.h"

int main()
{
   std::cout << "Main Entered" << std::endl;


   switch(0)
   {
      case test_int:
         std::cout << "Case X" << std::endl;
         break;
      default:
         std::cout << "Case Default" << std::endl;
         break;
   }

   return 0;
}

x_def.h

extern const int test_int;

x_def.cpp

const int test_int = 0;

このコードは、Visual C++ 2008 で正しくコンパイルされます。さらに、私のモンタナの友人が ISO C++ 標準をチェックしたところ、どの const-integer 式も機能するはずです。これはおそらくコンパイラのバグですか、それとも明らかな何かを見逃していますか?

コンパイラのバージョン情報は次のとおりです。

C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/specs から仕様
を読み取る: ../gcc-3.4.5-20060117-3/configure --with-gcc --with- gnu-ld --with-gnu-as --host=mingw32 --target=mingw32 --prefix=/mingw --enable-threads --disable-nls --enable-languages=c,c++,f77,ada, objc,java --disable-win32-registry --disable-shared --enable-sjlj-exceptions --enable-libgcj --disable-java-awt --without-x --enable-java-gc=boehm -- disable-libgcj-debug --enable-interpreter --enable-hash-synchronization --enable-libstdcxx-debug
スレッド モデル: win32
gcc バージョン 3.4.5 (mingw-vista スペシャル r3)

4

8 に答える 8

5

caseラベルには、使用時にコンパイル時に値を決定できるようにする厳密な要件を持つ整数定数式が必要です。

5.19 [expr.const] から、「整数定数式には、定数式(8.5) で初期化された整数型または列挙型のリテラル (2.13)、列挙子、const 変数、または静的データ メンバーのみを含めることができます...」。

test_int定数式が必要な場所で使用する時点で、それはconst宣言された変数でexternあり、初期化子がなく、定数式の要件を満たしていません。実際には別の整数定数式で初期化するという事実にもかかわらず翻訳単位。(*これは標準の文言から完全に明確ではありませんが、現在の私の解釈です。)

標準の制限では、次のような使用は許可されていません。

void f(int a, int b)
{
    const int c = b;

    switch (a)
    {
    case c:
        //...
    }
}

あなたの例では、コンパイラが をコンパイルしtest.cppているとき、初期化子が何にあるのかを判断する方法がありませんx_def.cpp。あなたがしたかもしれません:

const int test_int = (int)time();

明らかに、これらの例のいずれにおいても、const intコンパイル時に の値を決定できませんでした。これは、整数定数式の意図です。

于 2009-12-08T21:41:46.390 に答える
4

ケース ラベルはコンパイル時の定数でなければなりません。つまり、コンパイラはコンパイル時に値を代入できなければなりません。値は定数ですが、コンパイラは少なくともリンク時まで値を知ることができません。

于 2009-12-08T21:34:45.967 に答える
3

VC++ は正しく、g++ は間違っています。ケース ラベルはintegral constant expression(§6.4.2/2) である必要があり、定数式で初期化された整数型の const 変数は定数式です (§5.19/1)。

編集:主に Pavel と、DR の可能性についての彼の提案。§5.19/2 はすでに完全に書き直されています。C++0x は、定数式と見なされるものを大幅constexpr拡張する、まったく新しい a の概念を追加します。たとえば、現在の標準では次のようになります。

int x() { return 10; }
const int y = x();

y定数式ではありません。リテラルで (間接的に) 初期化されていることは誰でも簡単にわかります10が、コンパイラはそれを定数式として許可することはできません。新しい規格では、 として指定することが可能にx()なり、定数式になります。constexpry

N2960 で定式化されているように、§5.19/2 は、次のリストから何かを使用しない限り、式は定数式であると述べています。次に、ページ長のリストについて説明しますconstが、現在のコンパイル単位で初期化されていない変数を使用することは、それらの1つではないようです。[編集: 以下を参照してください -- CWG Issue 721 を読んで、考えが変わりました。]

VC++ が正しくて g++ が間違っているという限り、私はこの非常に具体的な点でのみ意味していました。標準のすべての部分を正しくすることについて話しているのであれば、どちらも「間違っている」ことに疑いの余地はありません。どちらかの実装に取り​​組んでいる人がいるとは思えませんexport

exportただし、C++ がリンク時まで決定を延期することをいとわないように見える程度を指摘しています。2 フェーズの名前検索とは、エクスポートされたテンプレートがコンパイルされるときに、定数式だけでなく、確実にわからないことがたくさんあることを意味します。特定の名前が関数を参照しているのかオブジェクトを参照しているのかさえわからないかもしれませんが、標準が正確にそれを要求していることは間違いありません。当面の問題は、対処するのがかなり簡単なものだと思います。

編集: 少し検索したところ、Core Working Group Issue 721 が見つかりました。James の質問は、手元にあるものと非常によく似ています (「しかし、これは、初期化が同じ翻訳単位で行われ、定数式の前に...")。提案された解決策には、「...前に初期化を行う...」というフレーズが追加されています。少なくとも私が読んだ限りでは、委員会は、現在の標準ではコードを受け入れなければならないが、新しい標準では許可されないことに同意したことを意味します。

その文言は今年の 7 月に合意されましたが、C++0x の最新のドラフトであると思われる N2960 には (まだ?) 表示されていません。

于 2009-12-08T21:40:51.397 に答える
1

ここでは、MS コンパイラーが少しやんちゃです。同じコンパイル単位で定数を使用して定数の初期化と case ステートメントをコンパイルすると、コンパイル時に定数値が計算されます。

extern const 初期化されたコンパイル単位の外側(つまり、初期化を含む cpp ファイルまたはそれに含まれるファイル)を使用しようとすると、コンパイラはほぼ同じエラーで barf します。Fred Larson は正しいです。コンパイラはリンク時まで定数値を認識すべきではありません。したがって、スイッチ定数として受け入れられるべきではありません。MS コンパイラが少し不正を行っているだけです。

#define問題の解決策はマクロを使用することですが、定数を使用したくない理由はありますか?

于 2009-12-08T22:00:39.403 に答える
1

VC++2008 を使用した単純な例ではこれを再現できません。

test.cpp:

extern const int n;
int main() {
    switch (0) {
    case n: break;
    }
}

test2.cpp:

extern const int n = 123;

次のようにコンパイルします。

cl.exe test.cpp test2.cpp

出力:

test.cpp(4) : エラー C2051: ケース式が定数ではありません

于 2009-12-08T22:02:52.260 に答える
0

より簡単なテストは次のとおりです。

test_int.cpp:

const int test_int = 10;

main.cpp:

#include <iostream>
using std::cout;
using std::endl;

extern const int test_int;

int main() {
    cout << test_int << endl;
    return 0;
}

G ++では、未定義の参照を取得します。ただし、Cで同じことを行うと機能します。http://gcc.gnu.org/ml/gcc/2005-06/msg00325.htmlによると、const変数はC++で暗黙的に内部リンケージを持っています。これはCでは当てはまらないようです。

于 2009-12-08T21:48:33.937 に答える
0

私は「gcc(SUSE Linux)4.3.2」を使用していて、同様の効果がありますが、それでも少し奇妙です。

私の定義は次のとおりです。

namespace operations{
   const cOpDummy OpDummy();
   const cInitOperator InitOperator();
};

const unsigned long ulNumberOfOperations = 2;

const cOperation * arrayOperations[] = {
   & (operations::OpDummy),
   & (operations::InitOperator)
};

また、他のファイルのextern宣言は次のとおりです。

extern const unsigned long ulNumberOfOperations;
extern const cOperation * arrayOperations[];

面白いことに、コンパイラは「ulNumberOfOperations」「ulNumberOfOperationsへの未定義の参照」だけを提供しますが、「arrayOperations[]」では問題ありません。私の回避策は、「ulNumberOfOperations」を定数ではないと宣言することです。

于 2010-04-01T16:50:06.147 に答える