Defining global constant in C++
I want to define a constant in C++ to be visible in several source files. I can imagine the following ways to define it in a header file:
#define GLOBAL_CONST_VAR 0xFF
int GLOBAL_CONST_VAR = 0xFF;
- Some function returing the value (e.g.
int get_GLOBAL_CONST_VAR()
) enum { GLOBAL_CONST_VAR = 0xFF; }
const int GLOBAL_CONST_VAR = 0xFF;
-
extern const int GLOBAL_CONST_VAR;
and in one source fileconst int GLOBAL_CONST_VAR = 0xFF;
Option (1) - is definitely not the option you would like to use
Option (2) - defining instance of the variable in each object file using the header file
Option (3) - IMO is over killing in most cases
Option (4) - in many cases maybe not good since enum has no concrete type (C++0X will add possibility to define the type)
So in most cases I need to choose between (5) and (6). My questions:
- What do you prefer (5) or (6)?
- Why (5) is ok, while (2) is not?
Definitely go with option 5 - it's type safe and allows compiler to optimize (don't take address of that variable :) Also if it's in a header - stick it into a namespace to avoid polluting the global scope:
// header.hpp
namespace constants
{
const int GLOBAL_CONST_VAR = 0xFF;
// ... other related constants
} // namespace constants
// source.cpp - use it
#include <header.hpp>
int value = constants::GLOBAL_CONST_VAR;
(5) says exactly what you want to say. Plus it lets the compiler optimize it away most of the time. (6) on the other hand won't let the compiler ever optimize it away because the compiler doesn't know if you'll change it eventually or not.
(5) is "better" than (6) because it defines GLOBAL_CONST_VAR
as an Integral Constant Expression (ICE) in all translation units. For example, you will be able to use it as array size and as case label in all translation units. In case of (6) GLOBAL_CONST_VAR
will be an ICE only in that translation unit where it is defined and only after the point of definition. In other translation units it won't work as ICE.
However, keep in mind that (5) gives GLOBAL_CONST_VAR
internal linkage, meaning that the "address identity" of GLOBAL_CONST_VAR
will be different in each translation unit, i.e. the &GLOBAL_CONST_VAR
will give you a different pointer value in each translation unit. In most usage cases this doesn't matter, but if you'll need a constant object that has consistent global "address identity", then you'd have to go with (6), sacrificing the ICE-ness of the constant in the process.
Also, when the ICE-ness of the constant is not an issue (not an integral type) and the size of the type grows larger (not a scalar type), then (6) usually becomes a better approach than (5).
(2) is not OK because the GLOBAL_CONST_VAR
in (2) has external linkage by default. If you put it in header file, you'll usually end up with multiple definitions of GLOBAL_CONST_VAR
, which is an error. const
objects in C++ have internal linkage by default, which is why (5) works (and which is why, as I said above, you get a separate, independent GLOBAL_CONST_VAR
in each translation unit).
Starting from C++17 you have an option of declaring
inline extern const int GLOBAL_CONST_VAR = 0xFF;
in a header file. This gives you an ICE in all translation units (just like method (5)) at the same time maintaining global address identity of GLOBAL_CONST_VAR
- in all translation units it will have the same address.
If you use C++11 or later, try using compile-time constants:
constexpr int GLOBAL_CONST_VAR{ 0xff };