我正在使用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)
答案 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()
指定为constexpr
,y
将是常量表达式。
由于它是在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