Say that I have a class that requires a few constants to function. Several member functions require use of these constants. Use of #define is frowned upon since it can cause collisions. The constants are hex patterns of 8 or 16 bits and are stored as uint8_t or uint16_t. These constants also don't change from instance to instance of the class, and therefore memory (albeit very little memory) can be saved by having only one copy of the constants.
Is there anything improper, or perhaps of better way of accomplishing the above instead of simply doing something like the following:
// mycode.h
// .......
class myclass {
private:
static const uint16_t kMyClassConstant_ = 0xBEEF;
// .......
};
Thanks in advance for the help.
Given your description of the situation, I'd say using static const
members is a good approach. In C++11 you may want to change it into static constexpr
to emphasize it's a compile-time constant, although nothing will effectively change as a result of that.
If you refer to myclass::kMyClassContant_
somewhere in the code in a way that is relevant under the one-definition-rule (odr), esp. in contexts that require a reference (including const-reference), the compiler will complain that there is no definition of the constant. Merely declaring and initializing it inside the class isn't sufficient in this case. This may force you to separate declaration and definition:
// mycode.h
class myclass {
private:
static const uint16_t kMyClassConstant_;
};
// mycode.cpp
const uint16_t myclass::kMyClassConstant_ = 0xBEEF;
To avoid the trouble of maintaining separate declarations and definitions, some people prefer declaring an inline constexpr function instead of an actual variable:
// mycode.h
class myclass {
private:
static constexpr uint16_t kMyClassConstant_()
{ return 0xBEEF; }
};
This is a correct work-around for many of the odr-related problems, and it does not cause any loss in performance. Whether it is really useful depends on how much of a burden it is to maintain separate declarations and definitions of an ordinary static constant. If you expect your constants to never change as your code evolves, using ordinary static constants with separate definitions is preferable. But if you modify the definitions of the constants frequently, having to re-compile the definition file and re-link it to all relevant parts of the project may make you consider the function-based solution above as a better alternative.
A final comment on the data type: Forcing it into 16 bits using std::uint16_t
can be useful if you need to store lots of these values in compact form. Otherwise, the actual size may not really matter, in which case std::uint_fast16_t
(which may be larger than 16 bits) may be better.
You could use type traits to implement this:
#include <type_traits>
class myclass {
private:
typedef std::integral_constant<uint16_t , 0xBEEF> kMyClassConstant;
// ...
};
used as myclass::kMyClassConstant::value
.
This shows the purpose of implementing an integral constant and prevents you from accidentaly taking an address of the constant.
Since C++17, we have access to inline
variables, which take care of the odr-related problems. Several options:
// mycode.h
class myclass {
static const inline uint16_t kMyClassConstant_ = 0xBEEF;
};
Or, if it can be marked constexpr
(like in this case):
// mycode.h
class myclass {
static constexpr inline uint16_t kMyClassConstant_ = 0xBEEF;
};
Which can be simplified to:
// mycode.h
class myclass {
static constexpr uint16_t kMyClassConstant_ = 0xBEEF;
};
Because in C++17 constexpr
implies inline
for static
data members.