我最近在一些正在开发的大型程序中发现了一个烦人的问题;我想了解如何以最佳方式解决它。我将代码剪切到以下最小的示例。
#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; else stuff = MagicNumbers::BIG;
我最喜欢第一个选项(但是,它并不理想,因为我们实际上对这些常量使用uint32_t而不是int,而enum与int同义)。但我真正想问的是:它的错误是什么?
我不应该理解不了解静态积分常数是如何工作的吗?
我应该责怪gcc并希望修复(或者最新版本已经有修复,或者可能有一个模糊的命令行参数使这项工作)?
与此同时,我只是使用优化编译我的代码,调试很痛苦:-O3
答案 0 :(得分:20)
This is a known issue。标准是责备或你没有提供静力学的定义。根据您的观点:)
答案 1 :(得分:7)
C ++中的静态数据成员don't work like that:
静态数据成员不属于 给定类类型的对象;他们 是单独的对象。结果, 静态数据成员的声明是 不被视为定义。数据 成员在类范围内声明,但是 定义在文件范围内执行。 这些静态成员有外部 键。
你只是声明那些常量,即使你正在初始化它们。您仍然必须在命名空间范围定义它们:
class MagicNumbers
{
public:
static const int BIG = 100;
static const int SMALL = 10;
};
const int MagicNumbers::BIG;
const int MagicNumbers::SMALL;
这将消除链接错误。
答案 2 :(得分:7)
尽管有传统的建议,我发现static const int ...
总是给我带来比好老enum { BIG = 100, SMALL = 10 };
更令人头疼的问题。在C ++ 11提供强类型枚举的情况下,我现在使用static const int ...
的原因更少了。
答案 3 :(得分:3)
嘿,根据C ++标准,9.4.2(class.static.data):
如果静态数据成员是const 字面类型,它的声明在 类定义可以指定一个 支撑或等于初始化器 每个初始化子句都是一个 assignment-expression是一个常量 表达。静态数据成员 文字类型可以在中声明 使用constexpr进行类定义 符;如果是这样,它的声明 应指明一个 支撑或等于初始化器 每个初始化子句都是一个 assignment-expression是一个常量 表达。 [注意:在这两个方面 案件,会员可能会出现在 常数表达式。 - 结束注释] 会员仍应在a。中定义 命名空间作用域,如果它在 程序和命名空间范围 定义不得包含 初始化程序。强>
所以声明是正确的,但你仍然需要在某个地方有一个定义。我一直认为你可以掌握这个定义,但我认为这不符合标准。
答案 4 :(得分:1)
我很难断言这是任何人的错误。
在声明点给定值的静态const积分不是变量,它们是常量表达式。对于有变量,您仍然需要定义它。
三元运算符的规则非常复杂,可能必然如此,实际上并没有真正说明常量表达式,只有rvalues;很明显,编译器认为它们应该是变量,除非优化得到提升。我认为可以自由地解释表达式(作为常量表达式或变量)。
答案 5 :(得分:1)
我是C ++的新手,但我认为你的类声明只声明那些静态成员存在,你仍然需要在某处定义它们:
class MagicNumbers
{
public:
static const int BIG;
static const int SMALL;
};
const int MagicNumbers::BIG = 100;
const int MagicNumbers::SMALL = 10;
更高的优化级别可能包括足够的静态分析级别,以确定BIG
和SMALL
可以与其实际值交换,而不是为它们提供任何实际存储(语义将是因此,在这种情况下定义这些变量将是多余的,因此它链接正常。
答案 6 :(得分:0)
你仍然需要在某处为他们分配空间:
class MagicNumbers
{
public:
static const int BIG = 100;
static const int SMALL = 10;
};
const int MagicNumbers::BIG;
const int MagicNumbers::SMALL;
答案 7 :(得分:0)
为什么你的神奇数字在课堂上?
namespace MagicNumbers {
const int BIG = 100;
const int SMALL = 10;
}
问题解决了,无需担心C ++标准中的缺陷。