C++积分常数 +选择运算符=问题!
我最近在我正在开发的一些大型程序中发现了一个恼人的问题;我想了解如何以最好的方式解决它。我将代码缩减为以下最小示例。
#include <iostream>
using std::cin;
using std::cout;
class MagicNumbers
{
public:
static const int BIG = 100;
static const int SMALL = 10;
};
int main()
{
int choice;
cout << "How much stuff do you want?\n";
cin >> choice;
int stuff = (choice < 20) ? MagicNumbers::SMALL : MagicNumbers::BIG; // PROBLEM!
cout << "You got " << stuff << "\n";
return 0;
}
使用 -O0 或 -O1 编译时,我在 gcc 4.1.2 中遇到链接错误,但使用 -O2 或 -O3 编译时一切正常。无论优化选项如何,它都可以与 MS Visual Studio 2005 很好地链接。
test.cpp:(.text+0xab): 对“MagicNumbers::SMALL”的未定义引用
test.cpp:(.text+0xb3): 对 `MagicNumbers::BIG' 的未定义引用
我查看了中间汇编代码,是的,未优化的代码将SMALL和BIG视为外部int变量,而优化的代码则使用实际数字。以下每项更改都修复了该问题:
使用 enum 而不是 int 作为常量:
enum {SMALL = 10}
在每次使用时强制转换常量(任意一个):
(int)MagicNumbers::SMALL
或(int)MagicNumbers::BIG
甚至MagicNumbers::SMALL + 0
使用宏:
#define SMALL 10
不使用选择运算符:
if (choice < 20) stuff = MagicNumbers::SMALL;其他东西= MagicNumbers::BIG;
我最喜欢第一个选项(但是,它并不理想,因为我们实际上使用 uint32_t 而不是 int 作为这些常量,而 enum 与 int 同义)。但我真正想问的是:这是谁的bug?
我是因为不了解静态积分常量如何工作而受到责备的人吗?
我应该责怪 gcc 并希望得到修复(或者可能最新版本已经有修复,或者可能有一个晦涩的命令行参数来使这项工作起作用)?
同时,我只是通过优化来编译我的代码,调试起来很痛苦:-O3
I have recently discovered an annoying problem in some large program i am developing; i would like to understand how to fix it in a best way. I cut the code down to the following minimal example.
#include <iostream>
using std::cin;
using std::cout;
class MagicNumbers
{
public:
static const int BIG = 100;
static const int SMALL = 10;
};
int main()
{
int choice;
cout << "How much stuff do you want?\n";
cin >> choice;
int stuff = (choice < 20) ? MagicNumbers::SMALL : MagicNumbers::BIG; // PROBLEM!
cout << "You got " << stuff << "\n";
return 0;
}
I get link errors in gcc 4.1.2 when compiling with -O0 or -O1 but everything is OK when compiling with -O2 or -O3. It links well using MS Visual Studio 2005 regardless of optimization options.
test.cpp:(.text+0xab): undefined reference to `MagicNumbers::SMALL'
test.cpp:(.text+0xb3): undefined reference to `MagicNumbers::BIG'
I looked at the intermediate assembly code, and yes, the non-optimized code regarded SMALL and BIG as external int variables, while the optimized one used the actual numbers. Each of the following changes fixes the problem:
Use enum instead of int for constants:
enum {SMALL = 10}
Cast the constant (any one) at each usage:
(int)MagicNumbers::SMALL
or(int)MagicNumbers::BIG
or evenMagicNumbers::SMALL + 0
Use a macro:
#define SMALL 10
Not use the choice operator:
if (choice < 20) stuff = MagicNumbers::SMALL; else stuff = MagicNumbers::BIG;
I like the first option best (however, it's not ideal because we actually use uint32_t instead of int for these constants, and enum is synonymous with int). But what i really want to ask is: whose bug is it?
Am i the one to blame for not understanding how static integral constants work?
Should i blame gcc and hope for a fix (or maybe the latest version already has a fix, or maybe there is an obscure command-line argument to make this work)?
Meanwhile, i just compile my code with optimizations, and it's a pain to debug :-O3
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我是 C++ 新手,但我认为您的类声明仅声明这些静态成员存在,您仍然需要在某处定义它们:
较高的优化级别可能包括足够彻底的静态分析级别,以确定
BIG
和SMALL
可以与它们的实际值进行交换,并且不给它们任何实际存储(语义将是相同的),因此在这种情况下定义这些变量将是多余的,因此它链接确定。I'm new to C++, but I think that your class declaration only declares that those static members exist, you still need to define them somewhere:
The higher optimisation levels probably include a level of static analysis thorough enough to determine that
BIG
andSMALL
can be exchanged with their actual values and not to give them any actual storage (the semantics will be the same), so defining these variables in this circumstance would be redundant, hence it links OK.我很难断言这是任何人的错误。
在声明时给定值的静态常量积分不是变量,它们是常量表达式。为了有一个变量,你仍然需要定义它。
关于三元运算符的规则非常复杂,可能确实如此,并且实际上并没有真正说明有关常量表达式的任何内容,仅说明了右值;显然,编译器认为它们应该是变量,除非优化得到提高。我认为可以自由地以任何方式解释表达式(作为常量表达式或作为变量)。
I'd be hard pressed to assert that it's anyone's bug.
Static const integrals given values at point of declaration are not variables, they're constant expressions. For there to be a variable you still need to define it.
The rules wrt the ternary operator are pretty absurdly complex, probably necessarily so, and actually doesn't really say anything about constant expressions, only rvalues; obviously the compiler thinks they should be variables unless optimization is cranked way up. I think it's free to interpret the expression either way (as a constant expression or as variable).
您仍然需要在某处为它们分配空间:
You still need to allocate space for them somewhere:
为什么你的神奇数字在课堂上?
问题已解决,无需担心 C++ 标准中的缺陷。
Why are your magic numbers in a class?
Problem solved without needing to worry about flaws in the C++ standard.
这是一个已知问题。该标准应该归咎于您,因为它没有提供静力学的定义。取决于你的观点:)
This is a known issue. The Standard is to blame or you for not providing a definition of the statics. Depending on your point of view :)
尽管有传统的建议,我发现
static const int ...
总是比旧的enum { BIG = 100, SMALL = 10 }; 更让我头疼。 。随着 C++11 提供强类型枚举,我现在使用
static const int ...
的理由就更少了。In spite of the conventional advice, I have found that
static const int ...
invariably gives me more headaches than good oldenum { BIG = 100, SMALL = 10 };
. And with C++11 providing strongly-typed enums, I now have even less cause to usestatic const int ...
.静态数据成员在 C++ 中的工作方式不同:
即使您正在初始化这些常量,您也只是声明它们。您仍然必须在命名空间范围内定义它们:
这将消除链接错误。
Static data members don't work like that in C++:
You're only declaring those constants, even though you're initializing them. You still have to define them at namespace scope:
That will get rid of the link errors.
呵呵,根据C++标准,9.4.2(class.static.data):
所以声明是正确的,但您仍然需要在某个地方有一个定义。我一直认为你可以掌握这个定义,但我认为这不符合标准。
Heh, according to the C++ standard, 9.4.2 (class.static.data):
So the declaration is correct, but you still need to have a definition somewhere. I always thought you could skill the definition, but I suppose that isn't standard conforming.