C++ Switch 不会使用外部定义的变量作为 case 进行编译
我正在使用 MinGW GNU 编译器编写 C++,当我尝试使用外部定义的整数变量作为 switch 语句中的 case 时,就会出现问题。我收到以下编译器错误:“case 标签不会简化为整数常量”。
因为我已将整数变量定义为 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++ 标准,发现任何常量整数表达式都应该有效。这可能是编译器错误还是我错过了一些明显的东西?
这是我的编译器版本信息:
从 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 --启用-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)
I'm writing C++ using the MinGW GNU compiler and the problem occurs when I try to use an externally defined integer variable as a case in a switch statement. I get the following compiler error: "case label does not reduce to an integer constant".
Because I've defined the integer variable as extern I believe that it should compile, does anyone know what the problem may be?
Below is an example:
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;
This code will compile correctly on Visual C++ 2008. Furthermore a Montanan friend of mine checked the ISO C++ standard and it appears that any const-integer expression should work. Is this possibly a compiler bug or have I missed something obvious?
Here's my compiler version information:
Reading specs from C:/MinGW/bin/../lib/gcc/mingw32/3.4.5/specs
Configured with: ../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
Thread model: win32
gcc version 3.4.5 (mingw-vista special r3)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
case
标签需要一个积分常量表达式,它具有严格的要求,使其值能够在编译时和使用时确定。从 5.19 [expr.const] 开始,“整型常量表达式只能涉及文字 (2.13)、枚举器、const 变量或整型或枚举类型的静态数据成员用常量表达式初始化(8.5),...”。
当您在需要常量表达式的地方使用
test_int
时,它是一个声明为extern
的const
变量,并且没有任何初始值设定项,并且不会满足常量表达式的要求,尽管事实上您确实使用另一个翻译单元中的整型常量表达式来初始化它。 (*从标准的措辞来看,这并不完全清楚,但这是我目前对它的解释。)标准中的限制不允许使用,例如:
在您的示例中,当编译器编译
test.cpp
,它无法确定 x_def.cpp 中的初始化程序可能是什么。您可能已经这样做了:显然,在这两个示例中,const int 的值都无法在编译时确定,而这正是整型常量表达式的目的。
A
case
label requires an integral constant expression which have strict requirements that enable their value to be determined at compile time at the point of use.From 5.19 [expr.const], "an integral constant expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5),...".
At the point at which you use
test_int
where a constant expression is required, it is aconst
variable declaredextern
and without any initializer and does not meet the requirements for a constant expression, despite the fact that you do actually initialize it with a integral constant expression in another translation unit. (*This is not completely clear from the wording of the standard but is my current interpretation of it.)The restrictions in the standard disallow usages such as:
In your example, when the compiler is compiling
test.cpp
, it has no way to determine what the initializer might be inx_def.cpp
. You might have done:Clearly, in neither of these examples could the value of the
const int
be determined at compile time which is the intention for integral constant expressions.大小写标签必须是编译时常量。这意味着编译器必须能够在编译时替换该值。尽管您的值是常量,但编译器至少要到链接时才能知道它们的值。
Case labels have to be compile-time constants. That means the compiler must be able to substitute the value at compile-time. Although your values are constant, the compiler can't know their values until at least link-time.
VC++是对的,g++是错的。 case 标签必须是
整型常量表达式
(第 6.4.2/2 节),并且用常量表达式初始化的整数类型 const 变量是常量表达式(第 5.19/1 节)。编辑:主要是针对帕维尔,以及他对可能的博士的建议。 §5.19/2 已被完全重写。 C++0x 添加了一个全新的 constexpr 概念,它大大扩展了常量表达式的内容。例如,在当前标准下,类似:
y
不是常量表达式。我们都可以很容易地看到它是用文字10
(间接)初始化的,但是编译器仍然不允许它作为常量表达式。在新标准下,可以将x()
指定为constexpr
,而y
将指定为常量表达式。正如 N2960 中的表述,§5.19/2 表示表达式是常量表达式,除非它使用以下列表中的内容。然后它给出了一个长达一页的列表,但使用未在当前编译单元中初始化的 const 变量似乎不是其中之一。 [编辑:见下文——阅读 CWG 第 721 期,我改变了主意。]
至于 VC++ 是正确的而 g++ 是错误的,我的意思只是在这个非常具体的方面。如果您谈论的是使标准的每个部分都正确,那么毫无疑问两者都是“错误的”。我怀疑是否有人正在致力于为其中任何一个实现
export
。然而,
export
确实指出了 C++ 似乎愿意将决策推迟到链接时间的程度。两阶段名称查找意味着当编译导出的模板时,除了常量表达式之外还有很多东西是不确定的。它甚至可能不知道特定的名称是指函数还是对象——但毫无疑问,标准确实确实需要这一点。在我看来,当前的问题是一个更容易处理的问题。编辑:我做了一些搜索,发现了核心工作组问题 721。Jame 的问题与手头的问题非常接近(“但是,这并不要求,因为它大概应该如此,初始化发生在同一个翻译单元中,并且在常量表达式之前...”)。拟议的决议添加了这样一句话:“……在前面进行初始化……”。至少在我读到的时候,这意味着委员会同意在当前标准下,该代码必须被接受,但在新标准下,这是不允许的。
该措辞已于今年 7 月达成一致,但(还没有?)出现在 N2960 中,我认为 N2960 是 C++0x 的最新草案。
VC++ is right, and g++ is wrong. A case label is required to be an
integral constant expression
(§6.4.2/2) and a const variable of integer type initialized with a constant expression is a constant expression (§5.19/1).Edit:mostly for Pavel, and his suggestion of a possible DR. §5.19/2 has been completely rewritten already. C++0x adds a whole new concept of a
constexpr
that expands what's considered a constant expression considerably. For example, under the current standard, something like:y
is not a constant expression. We can all easily see that it's (indirectly) initialized with a literal10
, but the compiler still can't allow it as a constant expression. Under the new standard, it'll be possible designatex()
as aconstexpr
, andy
will be a constant expression.As it's formulated in N2960, §5.19/2 says an expression is a constant expression unless it uses something from the following list. It then gives about a page-long list, but using a
const
variable that isn't initialized in the current compilation unit doesn't seem to be one of them. [Edit: see below -- reading CWG Issue 721, I've changed my mind.]As far as VC++ being right and g++ wrong, I meant only in this very specific respect. There's no question that both are "wrong" if you're talking about getting every part of the standard correct. I doubt anybody's even working on implementing
export
for either one.export
does, however, point out a degree to which C++ seems willing to postpone decisions until link time. Two-phase name lookup means that when an exported template is compiled, there's a lot more than just constant expressions that it doesn't know for sure. It might not even know whether a particular name refers to a function or an object -- but there's no question that the standard does require exactly that. The issue at hand strikes me as a substantially simpler one to deal with.Edit: I did a bit of searching, and found Core Working Group Issue 721. Jame's question parallels the one at hand quite closely ("However, this does not require, as it presumably should, that the initialization occur in the same translation unit and precede the constant expression..."). The proposed resolution adds the phrase: "...with a preceding initialization...". At least as I read it, that means that the committee agreed that under the current standard, the code must be accepted, but under the new standard it's not allowed.
That wording was agreed upon in July of this year, but doesn't (yet?) appear in N2960, which I believe is the most recent draft of C++0x.
我无法使用 VC++2008 在一个简单的示例中重现此内容:
test.cpp:
test2.cpp:
编译为:
输出:
I cannot reproduce this on a trivial example using VC++2008:
test.cpp:
test2.cpp:
compile with:
output:
MS 编译器在这里有点顽皮。当您在同一编译单元中编译常量初始化和使用常量的 case 语句时,它会在编译时计算出常量值。
一旦您尝试使用初始化的编译单元(即包含初始化的 cpp 文件或其包含的任何文件)的
extern const
outside ,编译器将抛出几乎相同的错误。 Fred Larson 是正确的,编译器不应该知道链接时间之前的常量值,因此它不能被接受作为开关常量,这只是 MS 编译器的一点作弊。解决您的问题的方法是使用宏,您有什么理由不想
#define
常量吗?MS compiler is being a bit naughty here. When you compile the the constant initialization and the case statement using the constant in the same compilation unit it works out the constant value at compile time.
Once you attempt to use the
extern const
outside of the compilation unit where it's initialised (i.e. the cpp file containing initialization or any of the files it includes) the compiler will barf with pretty much the same error. Fred Larson is correct the compiler shouldn't know the constant value until link time and thus it must not be acceptable as a switch constant, it's just MS compiler cheats a little bit.The solution to your problem would be to use macros, is there any reason why you don't want to
#define
the constant?这是一个更简单的测试:
test_int.cpp:
main.cpp:
在 G++ 中,我得到一个未定义的引用。然而,在 C 中做同样的事情是可行的。根据 http://gcc.gnu.org/ml/gcc/ 2005-06/msg00325.html ,const 变量在 C++ 中隐式具有内部链接。在 C 中似乎并非如此。
Here's a simpler test:
test_int.cpp:
main.cpp:
In G++, I get an undefined reference. However, doing the same thing in C works. According to http://gcc.gnu.org/ml/gcc/2005-06/msg00325.html , a const variable implicitly has internal linkage in C++. This doesn't appear to be the case in C.
我正在使用“gcc (SUSE Linux) 4.3.2”并具有类似的效果,但仍然有点奇怪。
我的定义是:
另一个文件中的 extern 声明是:
有趣的是:编译器只为“ulNumberOfOperations”提供“对 ulNumberOfOperations 的未定义引用”,但对“arrayOperations[]”则可以。
我的解决方法是声明“ulNumberOfOperations”不是常量。
I'm using a "gcc (SUSE Linux) 4.3.2" and having a similar effect, that still is a bit stranger.
My definitions are:
And the extern declarations in an other file are:
The funny thing is: The compiler gives just for "ulNumberOfOperations" "undefined reference to ulNumberOfOperations", but is Ok with "arrayOperations[]".
My workaround is to declare "ulNumberOfOperations" not constant.
从 c++11 开始,您可以构建一个小模板框架来为您提供如下语法:
完整演示:
预期结果:
Since c++11 you could build a little template framework to give you a syntax like this:
Full demo:
expected results: