Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constant variables not working in header

if I define my constant varibles in my header like this...

extern const double PI = 3.1415926535; extern const double PI_under_180 = 180.0f / PI; extern const double PI_over_180 = PI/180.0f; 

I get the following error

1>MyDirectX.obj : error LNK2005: "double const PI" (?PI@@3NB) already defined in main.obj 1>MyDirectX.obj : error LNK2005: "double const PI_under_180" (?PI_under_180@@3NB) already defined in main.obj 1>MyDirectX.obj : error LNK2005: "double const PI_over_180" (?PI_over_180@@3NB) already defined in main.obj 1>MyGame.obj : error LNK2005: "double const PI" (?PI@@3NB) already defined in main.obj 1>MyGame.obj : error LNK2005: "double const PI_under_180" (?PI_under_180@@3NB) already defined in main.obj 1>MyGame.obj : error LNK2005: "double const PI_over_180" (?PI_over_180@@3NB) already defined in main.obj 

but If I remove those constants from the header and put them in the document that is including the header like this...

const double PI = 3.1415926535; const double PI_under_180 = 180.0f / PI; const double PI_over_180 = PI/180.0f; 

It works

Does anyone have Idea what I might be doing wrong ??

Thanks

like image 332
numerical25 Avatar asked Feb 24 '10 18:02

numerical25


People also ask

Do constants go in header files?

cpp . For this reason, constexpr variables cannot be separated into header and source file, they have to be defined in the header file. Given the above downsides, prefer defining your constants in the header file.

How do you include a const in C++?

To declare a constant member function, place the const keyword after the closing parenthesis of the argument list. The const keyword is required in both the declaration and the definition.

Should global variables be in header file?

The header file is crucial; it enables cross-checking between independent TUs (translation units — think source files) and ensures consistency. That's the best way to declare and define global variables.


2 Answers

The problem is that you define objects with external linkage in header file. Expectedly, once you include that header file into multiple translation units, you'll get multiple definitions of the same object with external linkage, which is an error.

The proper way to do it depends on your intent.

  1. You can put your definitions into the header file, but make sure that they have internal linkage.

    In C that would require an explicit static

    static const double PI = 3.1415926535;  static const double PI_under_180 = 180.0f / PI;  static const double PI_over_180 = PI/180.0f;  

    In C++ static is optional (because in C++ const objects have internal linkage by default)

    const double PI = 3.1415926535;  const double PI_under_180 = 180.0f / PI;  const double PI_over_180 = PI/180.0f;  
  2. Or you can put mere non-defining declarations into the header file and put the definitions into one (and only one) implementation file

    The declarations in the header file must include an explicit extern and no initializer

    extern const double PI;  extern const double PI_under_180;  extern const double PI_over_180;  

    and definitions in one implementation file should look as follows

    const double PI = 3.1415926535;  const double PI_under_180 = 180.0f / PI;  const double PI_over_180 = PI/180.0f;  

    (explicit extern in the definitions is optional, if the above declarations precede the definitions in the same translation unit).

Which method you will choose depends on your intent.

The first method makes it easier for the compiler to optimize the code, since it can see the actual value of the constant in each translation unit. But at the same time conceptually you get separate, independent constant objects in every translation unit. For example, &PI will evaluate to a different address in each translation unit.

The second method creates truly global constants, i.e. unique constant objects that are shared by the entire program. For example, &PI will evaluate to the same address in each translation unit. But in this case the compiler can only see the actual values in one and only one translation unit, which might impede optimizations.


Starting from C++17 you get the third option, which sort of combines "the best of both worlds": inline variables. Inline variables can be safely defined in header files despite having external linkage

inline extern const double PI = 3.1415926535;  inline extern const double PI_under_180 = 180.0f / PI;  inline extern const double PI_over_180 = PI/180.0f;  

In this case you get a named constant object whose initializer value is visible in all translation units. And at the same time the object has external linkage, i.e. it has a global address identity (&PI is the same in all translation units).

Granted, something like that might only be necessary for some exotic purposes (most use cases in C++ call for the first variant), but the feature is there.

like image 133
AnT Avatar answered Oct 17 '22 08:10

AnT


extern means the 'real' definition of the variable is elsewhere, and the compiler should trust that things will hook up at link time. Having the definition inline with the extern is weird and is what's munging up your program. If you want to have them be extern, just define them exactly once elsewhere in your program.

like image 40
Carl Norum Avatar answered Oct 17 '22 08:10

Carl Norum