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

发布于 2024-10-03 05:14:36 字数 1462 浏览 0 评论 0原文

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

#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 even MagicNumbers::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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

梦里°也失望 2024-10-10 05:14:37

我是 C++ 新手,但我认为您的类声明仅声明这些静态成员存在,您仍然需要在某处定义它们:

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

const int MagicNumbers::BIG = 100;
const int MagicNumbers::SMALL = 10;

较高的优化级别可能包括足够彻底的静态分析级别,以确定 BIGSMALL 可以与它​​们的实际值进行交换,并且不给它们任何实际存储(语义将是相同的),因此在这种情况下定义这些变量将是多余的,因此它链接确定。

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:

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

const int MagicNumbers::BIG = 100;
const int MagicNumbers::SMALL = 10;

The higher optimisation levels probably include a level of static analysis thorough enough to determine that BIG and SMALL 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.

怪我太投入 2024-10-10 05:14:37

我很难断言这是任何人的错误。

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

关于三元运算符的规则非常复杂,可能确实如此,并且实际上并没有真正说明有关常量表达式的任何内容,仅说明了右值;显然,编译器认为它们应该是变量,除非优化得到提高。我认为可以自由地以任何方式解释表达式(作为常量表达式或作为变量)。

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).

放赐 2024-10-10 05:14:37

您仍然需要在某处为它们分配空间:

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

You still need to allocate space for them somewhere:

class MagicNumbers
{
public:
  static const int BIG = 100;
  static const int SMALL = 10;
};
const int MagicNumbers::BIG;
const int MagicNumbers::SMALL;
猫弦 2024-10-10 05:14:37

为什么你的神奇数字在课堂上?

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

问题已解决,无需担心 C++ 标准中的缺陷。

Why are your magic numbers in a class?

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

Problem solved without needing to worry about flaws in the C++ standard.

[浮城] 2024-10-10 05:14:36

这是一个已知问题。该标准应该归咎于您,因为它没有提供静力学的定义。取决于你的观点:)

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 :)

舂唻埖巳落 2024-10-10 05:14:36

尽管有传统的建议,我发现 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 old enum { BIG = 100, SMALL = 10 };. And with C++11 providing strongly-typed enums, I now have even less cause to use static const int ....

尛丟丟 2024-10-10 05:14:36

静态数据成员在 C++ 中的工作方式不同

静态数据成员不属于
给定类类型的对象;他们
是单独的对象。结果,
静态数据成员的声明是
不被视为定义。数据
成员是在类范围内声明的,但是
定义是在文件范围内执行的。
这些静态成员具有外部
链接。

即使您正在初始化这些常量,您也只是声明它们。您仍然必须在命名空间范围内定义它们:

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

const int MagicNumbers::BIG;
const int MagicNumbers::SMALL;

这将消除链接错误。

Static data members don't work like that in C++:

Static data members are not part of
objects of a given class type; they
are separate objects. As a result, the
declaration of a static data member is
not considered a definition. The data
member is declared in class scope, but
definition is performed at file scope.
These static members have external
linkage.

You're only declaring those constants, even though you're initializing them. You still have to define them at namespace scope:

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

const int MagicNumbers::BIG;
const int MagicNumbers::SMALL;

That will get rid of the link errors.

奈何桥上唱咆哮 2024-10-10 05:14:36

呵呵,根据C++标准,9.4.2(class.static.data):

如果静态数据成员是 const
文字类型,其声明在
类定义可以指定一个
大括号或等于初始化器,其中
每个初始化子句都是
赋值表达式是一个常量
表达。静态数据成员
文字类型可以在
使用 constexpr 定义类
说明符;如果是这样,其声明
应指定一个
大括号或等于初始化器,其中
每个初始化子句都是
赋值表达式是一个常量
表达。 [注:在这两个
在这种情况下,该成员可能会出现在
常量表达式。 ——尾注]
成员仍应定义在
命名空间范围,如果它被用在
程序和命名空间范围
定义不应包含
初始化程序。

所以声明是正确的,但您仍然需要在某个地方有一个定义。我一直认为你可以掌握这个定义,但我认为这不符合标准。

Heh, according to the C++ standard, 9.4.2 (class.static.data):

If a static data member is of const
literal type, its declaration in the
class definition can specify a
brace-or-equal-initializer in which
every initializer-clause that is an
assignment-expression is a constant
expression. A static data member of
literal type can be declared in the
class definition with the constexpr
specifier; if so, its declaration
shall specify a
brace-or-equal-initializer in which
every initializer-clause that is an
assignment-expression is a constant
expression. [ Note: In both these
cases, the member may appear in
constant expressions. —end note ] The
member shall still be defined in a
namespace scope if it is used in the
program and the namespace scope
definition shall not contain an
initializer.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文