Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ integral constants + choice operator = problem!

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

like image 671
anatolyg Avatar asked Nov 17 '10 21:11

anatolyg


3 Answers

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

like image 145
Johannes Schaub - litb Avatar answered Nov 16 '22 07:11

Johannes Schaub - litb


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

like image 7
Marcelo Cantos Avatar answered Nov 16 '22 09:11

Marcelo Cantos


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.

like image 7
Frédéric Hamidi Avatar answered Nov 16 '22 07:11

Frédéric Hamidi