5

我正在使用 MinGW GNU 编译器编写 C++,当我尝试在 switch 语句中使用外部定义的整数变量作为 case 时,就会出现问题。我收到以下编译器错误:“ case label does not reduce to an integer constant ”。

因为我已经将整数变量定义为 extern 我相信它应该编译,有谁知道问题可能是什么?

下面是一个例子:

测试.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 version 3.4.5 (mingw-vista special r3)

4

8 回答 8

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在编译时确定 的值,而这是整数常量表达式的意图。

于 2009-12-08T21:41:46.390 回答
4

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

于 2009-12-08T21:34:45.967 回答
3

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

编辑:主要针对 Pavel,以及他对可能的 DR 的建议。§5.19/2 已经完全重写。C++0x 添加了一个全新的 a 概念,constexpr大大扩展了被认为是常量表达式的内容。例如,在当前标准下,类似:

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

y不是常量表达式。我们都可以很容易地看到它是(间接)用字面量初始化的10,但编译器仍然不允许它作为常量表达式。在新标准下,它可以指定x()constexpr, 并且y是一个常量表达式。

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

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

export然而,确实指出了 C++ 似乎愿意将决策推迟到链接时间的程度。两阶段名称查找意味着在编译导出的模板时,除了不确定的常量表达式之外,还有很多其他内容。它甚至可能不知道一个特定的名称是指一个函数还是一个对象——但毫无疑问,标准确实要求这样做。手头的问题让我觉得处理起来要简单得多

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

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

于 2009-12-08T21:40:51.397 回答
1

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

一旦您尝试使用初始化它的编译单元的extern const 外部(即包含初始化的 cpp 文件或它包含的任何文件),编译器将发出几乎相同的错误。Fred Larson 是正确的,编译器在链接时间之前不应该知道常量值,因此它不能被接受为开关常量,这只是 MS 编译器作弊。

您的问题的解决方案是使用宏,有什么理由不想要#define常量吗?

于 2009-12-08T22:00:39.403 回答
1

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

测试.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) : error C2051: case expression not constant

于 2009-12-08T22:02:52.260 回答
0

这是一个更简单的测试:

test_int.cpp:

const int test_int = 10;

主.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 回答
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
于 2017-01-16T09:55:49.413 回答