C++积分常数+选择运算符=问题!

ana*_*lyg 9 c++ portability

我最近在一些正在开发的大型程序中发现了一个烦人的问题; 我想了解如何以最佳方式解决它.我将代码剪切到以下最小的示例.

#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;
}
Run Code Online (Sandbox Code Playgroud)

当使用-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

Joh*_*itb 20

这是一个已知问题.标准是责备或你没有提供静力学的定义.根据你的观点:)


Fré*_*idi 7

静态数据成员不像 C++中那样工作:

静态数据成员不是给定类类型的对象的一部分; 它们是独立的对象.因此,静态数据成员的声明不被视为定义.数据成员在类范围内声明,但定义在文件范围内执行.这些静态成员具有外部链接.

你只是声明那些常量,即使你正在初始化它们.您仍然需要在命名空间范围内定义它们:

class MagicNumbers
{
public:
    static const int BIG = 100;
    static const int SMALL = 10;
};

const int MagicNumbers::BIG;
const int MagicNumbers::SMALL;
Run Code Online (Sandbox Code Playgroud)

这将摆脱链接错误.

  • 这里我们去:9.4.2(4)[Classes]:"如果一个静态数据成员是const整数或const枚举类型,它在类定义中的声明可以指定一个常量初始化器,它应该是一个整型常量表达式(5.19)在这种情况下,成员可以出现在整数常量表达式中.如果在程序中使用该成员,并且命名空间范围定义不包含初始化程序,则该成员仍应在命名空间范围内定义. (2认同)

Mar*_*tos 7

尽管有传统的建议,我发现static const int ...总是让我头疼而不是好老enum { BIG = 100, SMALL = 10 };.并且随着C++ 11提供强类型的枚举,我现在使用的原因更少static const int ....