代码之家  ›  专栏  ›  技术社区  ›  anatolyg

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

  •  9
  • anatolyg  · 技术社区  · 14 年前

    我最近在开发的某个大型程序中发现了一个恼人的问题;我想了解如何以最佳方式解决它。我将代码缩减为下面的最小示例。

    #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编译时,GCC4.1.2中出现链接错误,但使用-O2或-O3编译时一切正常。它使用msvisualstudio2005链接良好,而不考虑优化选项。

    test.cpp:(.text+0xab):对“MagicNumbers::SMALL”的未定义引用

    test.cpp:(.text+0xb3):未定义对“MagicNumbers::BIG”的引用

    我查看了中间汇编代码,是的,未优化的代码将大小视为外部int变量,而优化的代码则使用实际数字。以下每个更改都解决了该问题:

    • 对常量使用枚举而不是int: enum {SMALL = 10}

    • 每次使用时转换常量(任意一个): (int)MagicNumbers::SMALL (int)MagicNumbers::BIG 甚至 MagicNumbers::SMALL + 0

    • 使用宏: #define SMALL 10

    • 不使用choice运算符: if (choice < 20) stuff = MagicNumbers::SMALL; else stuff = MagicNumbers::BIG;

    我最喜欢第一个选项(但是,这并不理想,因为我们实际使用uint32_t而不是int来表示这些常量,enum是int的同义词)。但我真正想问的是:这是谁的虫子?

    我不明白静态积分常数是如何工作的,是不是该怪我?

    我应该责怪gcc并希望得到一个修复吗(或者最新版本已经有了修复,或者可能有一个模糊的命令行参数来实现这一点)?

    同时,我只是用优化来编译我的代码,调试起来很痛苦:-O3

    8 回复  |  直到 14 年前
        1
  •  7
  •   Marcelo Cantos    14 年前

    尽管有传统的建议,我还是发现 static const int ... 总是比老好人更让我头疼 enum { BIG = 100, SMALL = 10 }; . 用C++ 11提供强类型枚举,我现在使用的原因更少了。 静态常量。。。 .

        2
  •  20
  •   Johannes Schaub - litb    14 年前

    This is a known issue . 标准应该受到责备,或者你没有提供静态的定义。取决于你的观点:)

        3
  •  7
  •   Frédéric Hamidi    14 年前

    静态数据成员 don't work like that 在C++中:

    静态数据成员不是 给定类类型的对象;它们 是独立的对象。因此 静态数据成员的声明是 不被认为是定义。数据 成员在类作用域中声明,但是 定义在文件范围内执行。 这些静态成员具有外部 联系。

    你只声明那些常量,即使你正在初始化它们。你还得 定义 它们位于命名空间范围:

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

    这将消除链接错误。

        4
  •  3
  •   MSN    14 年前

    HEH,根据C++标准,94.2(Class,static .data):

    如果静态数据成员是const 文本类型,它在 类定义可以指定 大括号或相等的初始值设定项,其中 每个初始值设定项子句 赋值表达式是常量 表达。的静态数据成员 文本类型可以在 使用constexpr的类定义 说明符;如果是,则其声明 应指定 大括号或相等的初始值设定项,其中 每个初始值设定项子句 赋值表达式是常量 表达。[注:在这两种情况下 案例中,成员可能出现在 常量表达式。尾注] 这个 成员仍应定义为 命名空间作用域(如果在 程序和命名空间作用域 定义不应包含 初始值设定项。

    所以声明是正确的,但是你仍然需要在某个地方有一个定义。我一直以为你能熟练地定义,但我想那不是标准的一致性。

        5
  •  1
  •   dreamlax    14 年前

    我对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
  •  1
  •   Edward Strange    14 年前

    我很难断言这是谁的错误。

    静态常量积分在声明点给出的值不是变量,而是常量表达式。要有一个变量,你仍然需要定义它。

    与三元运算符相关的规则相当复杂,这可能是必然的,而且实际上并没有真正说明常量表达式,只有rvalues;很明显,编译器认为它们应该是变量,除非优化被大大提高。我认为可以任意解释表达式(作为常量表达式或变量)。

        7
  •  0
  •   cababunga    14 年前

    你仍然需要在某个地方为他们分配空间:

    class MagicNumbers
    {
    public:
      static const int BIG = 100;
      static const int SMALL = 10;
    };
    const int MagicNumbers::BIG;
    const int MagicNumbers::SMALL;
    
        8
  •  0
  •   Steve Jessop    14 年前

    为什么你的魔法数字在班上?

    namespace MagicNumbers {
        const int BIG = 100;
        const int SMALL = 10;
    }
    

    解决问题不需要担心C++标准中的缺陷。