As others have said, you might want to avoid using classes to store your constants in other files, and you might simply wish to store them in a header (possibly with a namespace, more on that later). Having said that, I do understand that you might want a constant that belongs to a class, so I'll cover that case as well later in the answer.
The simplest way is to declare and define the constant in a header on its own as such:
// YourHeader.h
#ifndef YOUR_HEADER_H
#define YOUR_HEADER_H
[...]
const int aConstNumber = 1;
static const int anotherConstNumber = 2;
constexpr int aConstExprNumber = 3;
static constexpr int anotherConstExprNumber = 4;
[...]
#endif
// main.cpp
#include <iostream>
#include "YourHeader.h"
int main()
{
// You can simply access the variables by name as so:
std::cout << aConstNumber << "\n"; // 1
std::cout << anotherConstNumber << "\n"; // 2
std::cout << aConstExprNumber << "\n"; // 3
std::cout << anotherConstExprNumber << std::endl; // 4
}
These variables all behave the same logically, but they work in different ways. A variable declared with the const
keyword is defined at runtime, and guaranties it won't change. A variable declared with the constexpr
keyword, however, is defined at compile time.
So, while this means that if you had multiple classes with something like an ID (which shouldn't change, but should be unique), you'd prefer const
over constexpr
(since the latter wouldn't work in that context).
Let's talk about static
as well. static
means that there is a single shared instance of that variable across your classes. So if the Bar
class has a static int ID
value, all instances of Bar
share the same ID
value. Changing it once changes it for all instances. Though if it's a constant, you won't be able to change it.
In your case, if it's to define constants that are pre-defined and won't change, I'd strongly recommend using constexpr
, since you can use those variables to do things you couldn't use a const
variable for (like defining the size of an array).
Before you go ahead and do that, however, consider the following class.
class Foo
{
public:
const int a = 5; // Valid use of const.
constexpr int b = 7; // Invalid use of constexpr, won't even compile!
static constexpr int c = 10; // Valid use of constexpr.
int arrA[a]; // ERROR: 'a' is defined at runtime, so you can't use it to define a size.
int arrB[b]; // ERROR: You couldn't even define 'b', so this is not going to work...
int arrC[c]; // VALID: 'c' is known by the compiler, and is guaranteed to only ever be 10!
}
It is worth noting that if you wanted to access a
or c
(the only two valid variables declared and defined in your class), you'd have to use the following syntax, rather than just their names:
// "Foo" is their class' name, and represents their parent namespace.
std::cout << Foo::a << "\n"; // 5
std::cout << Foo::c << std::endl; // 10
As you can see, constexpr int b = 7;
is invalid. Why though? Shouldn't it be seen by the compiler and work just fine? Well, no. You see, the issue is that maybe you'll never instantiate that class, right? const
variables are fine, because they're defined at runtime, this means they don't have to exist, they just can't change once you've given them a value. constexpr
on the other hand needs to be sure it'll exist, because it has to exist and be valid at compile time!
While declaring a constexpr
variable is perfectly fine in a header file, it doesn't work unless you use the static
keyword with it if you want to declare one in a class! Because the static
keyword will let the compiler know that, regardless of how you instantiate this class, the variable's value will never change and it will be known at compile time thanks to constexpr
!
I strongly recommend you read this post to understand just what static
and constexpr
do when combined.
- If you have "constants" that are only constant once instantiated, go with
const
.
- If you have constants that will never change and will always represent the same value (think of mathematical values like PI) go with
constexpr
.
- If all instances of a class should share a constant value, I'd recommend against using
static const
, since it would be defined at runtime, but it will always have the same value, right? Just use static constexpr
instead.
Finally, I mentioned namespaces at the start of this answer. Sometimes, you might want a group of associated constants that don't necessarily belong to a class. For instance, while PI is used in circles, it doesn't necessarily mean that I want to include the circle
class' header every time I want PI. But I don't want to have a "raw" PI variable name in my project's namespace! That's just asking for trouble. Instead, consider surrounding your constants in a namespace
block to emulate the syntax used to call a class' member!
// YourHeader.h
#ifndef YOUR_HEADER_H
#define YOUR_HEADER_H
namespace MyConstants
{
constexpr int a = 1;
constexpr int b = 2;
}
#endif
// main.cpp
#include <iostream>
#include "YourHeader.h"
int main()
{
std::cout << MyConstants::a << "\n"; // 1
std::cout << MyConstants::b << "\n"; // 2
}
Note that we didn't use static
with a namespace, we don't need to since we're not using a class
or struct
, we're simply wrapping them in the namespace
to avoid naming conflicts. static
would do something else here. See this post for more info on that.
Extra Note: While it is possible to declare a variable as a constexpr const
, I wouldn't recommend it as it does the same as simply declaring it constexpr
. Do note that constexpr
when combined with pointers will turn the pointer into a constant pointer to a constant value! See this post for more details on that.