C ++ Switch不会使用外部定义的变量进行编译

时间:2009-12-08 21:29:12

标签: c++ gnu

我正在使用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上正确编译。此外,我的Montanan朋友检查了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 special r3)

8 个答案:

答案 0 :(得分:5)

case标签需要整数常量表达式,它具有严格的要求,可以在编译时在使用点确定它们的值。

从5.19 [expr.const]开始,“整数常量表达式只能包含文字(2.13),枚举数,常量变量或整数或枚举类型的静态数据成员用常量初始化表达式(8.5),...“。

在您使用需要常量表达式的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的值,这是整数常量表达式的意图。

答案 1 :(得分:4)

案例标签必须是编译时常量。这意味着编译器必须能够在编译时替换该值。虽然您的值是常量,但编译器至少在链接时才能知道它们的值。

答案 2 :(得分:3)

VC ++是对的,而g ++是错误的。案例标签必须是integral constant expression(§6.4.2/ 2),并且用常量表达式初始化的整数类型的const变量是一个常量表达式(§5.19/ 1)。

编辑:主要针对帕维尔,以及他对可能的灾难恢复的建议。 §5.19/ 2已经完全重写。 C ++ 0x添加了一个constexpr的全新概念,扩展被认为是一个常量表达式。例如,根据当前标准,例如:

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

y不是常量表达式。我们都可以很容易地看到它(间接)用文字10初始化,但编译器仍然不能允许它作为常量表达式。根据新标准,可以将x()指定为constexpry将是常量表达式。

由于它是在N2960中制定的,§5.19/ 2表示表达式是一个常量表达式,除非它使用以下列表中的内容。然后它给出了一个页面长的列表,但是使用未在当前编译单元中初始化的const变量似乎不是其中之一。 [编辑:见下文 - 阅读CWG第721期,我改变了主意。]

至于VC ++是正确的,而g ++是错的,我的意思只是在这个非常具体的方面。毫无疑问,如果您正在谈论使标准的每个部分都正确,两个都是“错误的”。我怀疑任何人甚至都在为任何一个实施export

然而,

export确实指出了C ++似乎愿意将决定推迟到链接时间的程度。两阶段名称查找意味着在编译导出的模板时,除了不确定的常量表达式之外,还有很多。它甚至可能不知道某个特定名称是指一个函数还是一个对象 - 但毫无疑问,标准 确实需要它。手头的问题让我感到很难处理。

编辑:我做了一些搜索,发现了核心工作组问题721. Jame的问题与手头的问题非常接近(“但是,这并不需要,因为它可能应该,初始化发生在同一个翻译单元并在常量表达式之前...“)。拟议的决议增加了一句:“......具有先前的初始化......”。至少在我阅读时,这意味着委员会同意在现行标准下,代码必须被接受,但根据新标准,不允许。

该措辞是在今年7月达成的,但是(但是?)没有出现在N2960中,我认为这是最近的C ++ 0x草案。

答案 3 :(得分:1)

MS编译器在这里有点顽皮。当您使用同一编译单元中的常量编译常量初始化和case语句时,它会在编译时计算出常量值。

一旦您尝试使用初始化的编译单元的extern const (即包含初始化的cpp文件或其中包含的任何文件),编译器将使用漂亮的barf大致相同的错误。 Fred Larson是正确的,编译器不应该知道常量值,直到链接时间,因此它不能作为开关常量接受,它只是MS编译器作弊。

你的问题的解决方案是使用宏,你有什么理由不想#define常数?

答案 4 :(得分:1)

我无法使用VC ++ 2008在一个简单的例子中重现这一点:

TEST.CPP:

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

测试2.cpp:

extern const int n = 123;

编译:

cl.exe test.cpp test2.cpp

输出:

  

test.cpp(4):错误C2051:案例表达式不是常数

答案 5 :(得分: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中做同样的事情。根据{{​​3}},const变量隐式地在C ++中具有内部链接。在C中似乎不是这种情况。

答案 6 :(得分: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”不是常数。

答案 7 :(得分:0)

从c ++ 11开始,您可以构建一个小模板框架,为您提供如下语法:

void test(int a, int x, int y, int z)
{
    std::cout << "given " << a << ", choosing ";
    given(a)
        .when(x, [] { std::cout << "x\n"; })
        .when(y, [] { std::cout << "y\n"; })
        .when(z, [] { std::cout << "z\n"; })
        .when(any_other, [] { std::cout << "none of the above\n"; });
}

完整演示:

#include <iostream>


struct any_object {};
constexpr auto any_other = any_object {};

template<class Expr>
struct when_object
{

    template<class T, class F>
    constexpr when_object& when(T const& value, F&& f)
    {
        if (not executed and expr == value) {
            executed = true;
            f();
        }
        return *this;
    }

    template<class F>
    constexpr void when(any_object, F&& f)
    {
        if (not executed) {
            executed = true;
            f();
        }
    }

    Expr const& expr;
    bool executed = false;
};

template<class Expr>
constexpr auto given(Expr const& expr)
{
    return when_object<Expr> {expr};
}

void test(int a, int x, int y, int z)
{
    std::cout << "given " << a << ", choosing ";
    given(a)
        .when(x, [] { std::cout << "x\n"; })
        .when(y, [] { std::cout << "y\n"; })
        .when(z, [] { std::cout << "z\n"; })
        .when(any_other, [] { std::cout << "none of the above\n"; });
}


int main()
{
    test(4, 4, 5, 6);
    test(4, 3, 4, 5);
    test(4, 2, 3, 4);
    test(1, 2, 3, 4);
}

预期结果:

given 4, choosing x
given 4, choosing y
given 4, choosing z
given 1, choosing none of the above