Best way of defining a compile-time constant
Solution 1
Indeed, this is trickier than it might appear.
Just to explicitly restate the requirements:
- There should be no runtime computation.
- There should be no static, stack, or heap memory allocation except for the actual result. (Allocation of executable code is impossible to forbid, but ensure that any data storage needed by the CPU is private.)
In C++, expressions can be lvalues or prvalues (before C++11 and in C, rvalues correspond to the relevant concept). Lvalues refer to objects, hence they can appear on the Left-hand side of an assignment expression. Object storage and lvalue-ness is what we want to avoid.
What you want is an identifier, or id-expression, to evaluate to a prvalue.
Currently, only enumerators can do this, but as you observed, they leave something to be desired. Each enumeration declaration introduces a new, distinct type, so enum { Value = 123 };
introduces a constant which is not an integer, but its own unique type which converts to int
. This isn't the right tool for the job, although it works in a pinch.
You can use #define
, but that's a hack since it avoids the parser completely. You have to name it with all capital letters, and then ensure that the same all-caps name isn't used for anything else in the program. For library interfaces, such a guarantee is especially onerous.
The next best option is a function call:
constexpr int value() { return 123; }
Careful, though, as constexpr
functions can still be evaluated at runtime. You need to jump through one more hoop to express this value as a computation:
constexpr int value() {
/* Computations that do not initialize constexpr variables
(or otherwise appear in a constant expression context)
are not guaranteed to happen at compile time, even
inside a constexpr function. */
/* It's OK to initialize a local variable because the
storage is only temporary. There's no more overhead
here than simply writing the number or using #define. */
constexpr int ret = 120 + 3;
return ret;
}
Now, you can't refer to the constant as a name, it must be value()
. The function call operator might look less efficient, but it's the only current way to completely eliminate storage overhead.
Solution 2
I think you should consider the C++11 feature of specifying an underlying type for an enum, which applied to your example would be:
enum : unsigned int { Value = 123 };
this removes one of your two objections to using enums, namely, your inability to control which type would actually be used to represent them. It still doesn't allow non-integeral constants, though.
Related videos on Youtube
Malvineous
Updated on November 04, 2020Comments
-
Malvineous over 3 years
What's the best way of defining a simple constant value in C++11, such that there is no runtime penalty? For example: (not valid code)
// Not ideal, no type, easy to put in wrong spot and get weird errors #define VALUE 123 // Ok, but integers only, and is it int, long, uint64_t or what? enum { Value = 123 }; // Could be perfect, but does the variable take up memory at runtime? constexpr unsigned int Value = 123; class MyClass { // What about a constant that is only used within a class, so // as not to pollute the parent namespace? Does this take up // memory every time the class is instantiated? Does 'static' // change anything? constexpr unsigned int Value = 123; // What about a non-integer constant? constexpr const char* purpose = "example"; std::string x; std::string metadata() { return this->x + (";purpose=" purpose); } // Here, the compiled code should only have one string // ";purpose=example" in it, and "example" on its own should not // be needed. };
Edit
Since I've been told this is a useless question because there's no background behind it, here's the background.
I am defining some flags, so that I can do something like this:
if (value & Opaque) { /* do something */ }
The value of
Opaque
won't change at runtime, so as it is only needed at compile time it seems silly to have it end up in my compiled code. The values are also used inside a loop that runs multiple times for each pixel in an image, so I want to avoid runtime lookups that will slow it down (e.g. a memory access to retrieve the value of the constant at runtime.) This isn't premature optimisation, as the algorithm currently takes about one second to process an image, and I regularly have over 100 images to process, so I want it to be as fast as possible.Since people are saying this is trivial and not to worry about it, I'm guessing
#define
is as close as you can get to a literal value, so maybe that's the best option to avoid "overthinking" the problem? I guess general consensus is you just hope nobody ever needs to use the wordOpaque
or the other constants you want to use?-
Brian Bi about 9 yearsAre you sure you're so tight on memory that 4 bytes makes a difference?
-
Malvineous about 9 yearsWhy does everyone always say this? Then you post what you want and all you get are tumbleweeds because nobody can figure out what you want, apart from one person who says you should post a simple example with just the core points...sigh
-
Malvineous about 9 years@Brian: Sure four bytes isn't much, but do you leave unused variables littered around your code? Why bother removing them, compiler warnings can be disabled and a few bytes isn't much :-P Of course one reason is that if you use these values in an algorithm that loops many times, then having the value directly in the code removes one memory lookup on each access, making your algorithm faster (compiler optimisations notwithstanding.)
-
Lightness Races in Orbit about 9 yearsYes you should post a simple example with just the core points, but those core points should have some bearing on reality. This question does not indicate any actual problem. It just solicits opinion on something without actually defining any constraints, making it useless!
-
Malvineous about 9 yearsWell ok, I'm using the fields as flags, for example
if (value & SomeFlag) { /* do something */ }
. Rather than sayif (pixel & 2)
I would rather sayif (value & Opaque)
as it's easier to read, but the value ofOpaque
won't change at runtime. It's only needed at compile time and seems silly to embed it into my executable just because I want my code to be easy to read. It's also inside a loop that runs multiple times for each pixel, so I want to avoid runtime lookups that will slow it down. So I'm after the most elegant way to define the constant, with a focus on new C++11/14 features. -
Neil Kirk about 9 yearsMy practices haven't been updated for C++11 yet but I just use regular
const int a = 0;
in a namespace, sometimesstatic const int a = 0
in a class. These won't use up memory at run-time if not required to exist in memory. -
einpoklum over 7 years@Malvineous: Ok, done.
-
-
Malvineous about 9 yearsDoesn't really answer the question though. Why use
static
at all? How does this work withconst char*
values (the code in my question was pseudocode and won't compile) -
Malvineous about 9 yearsYes, it tells me
error: expected ‘)’ before ‘purpose’
in themetadata()
function. -
T.C. about 9 years@Malvineous string literal concatenation applies to, well, string literals.
-
T.C. about 9 years@Malvineous If you want the translation phase 6 string literal concatenation, then a macro is pretty much your only choice.
-
Potatoswatter about 9 yearsYou can go very wrong with this. ODR-using such a constant from within an inline function (such as a template) will violate the one-definition rule, because the name refers to a separate, different object in each translation unit.
-
Neil Kirk about 9 yearsIt's hard not to care about it when C++ has 53910 ways of defining constants.
-
vsoftco about 9 years@LightnessRacesinOrbit do you need the
const
? I thoughtconstexpr
impliesconst
when declaring an object. -
Potatoswatter about 9 years@vsoftco
constexpr
implies bothconst
andstatic
. Thestatic
part is dangerous, and it's even been deprecated in C. -
Lightness Races in Orbit about 9 years@vsoftco: True. As of C++14,
constexpr
member functions are not implicitlyconst
so, for consistency, I prefer to write it out explicitly for object declarations too. -
Lightness Races in Orbit about 9 years@NeilKirk: It's extremely easy not to care whether a 4-byte constant is baked into your executable or optimised out.
-
Neil Kirk about 9 years@LightnessRacesinOrbit It's not if you are a perfectionist with OCD. IT BOTHERS ME.
-
vsoftco about 9 yearsare you saying that simply
return 123 + 3;
is not performed at compile time? -
Potatoswatter about 9 years@vsoftco Not quite. But
constexpr
variables are always guaranteed to be treated as constant expressions whereasconstexpr
functions are not, and can sometimes be evaluated at runtime. Of course, for such a trivial operation, it doesn't make a difference. But if it wereint ret = INT_MAX + 3;
, that would be different because there would be signed integer overflow. Usingconstexpr
very liberally helps prevent such gotchas. -
Potatoswatter about 9 years@LightnessRacesinOrbit Yes, internal linkage means there is a different object in each TU. Two definitions of the same inline function will use those two different objects. If it's an ODR-use, such as passing by reference (or perfect forwarding), it violates the ODR rule.
-
Potatoswatter about 9 years@LightnessRacesinOrbit If you have hundreds of constants and you're programming an embedded system with only a few kilobytes of program space, then it becomes a problem. To say that C++ isn't used for such embedded programming is circular reasoning: C++ is capable of utmost efficiency as long as we watch the corner cases.
-
Lightness Races in Orbit about 9 years@Potatoswatter: Quote me saying that C++ isn't used for embedded programming. I dare you. I do it myself, habitually. Yet, until such a specific use case is specified as a constraint, we shall discuss the language in the terms of the abstract machine that it describes only and, in those circumstances, caring about this sort of thing is entirely folly.
-
Potatoswatter about 9 years@LightnessRacesinOrbit So, when the question is stated in abstract terms, you ask for a use-case, and when you get a use-case, you ask to talk about the abstract machine. ???
-
Lightness Races in Orbit about 9 years@Potatoswatter: Ehm?! I think you've fundamentally misunderstood something. Can you be more specific, so that we can resolve going forwards. Regards
-
Potatoswatter about 9 years… In purely abstract-yet-realistic terms, for any arbitrary program size constraint, there is some number of constants that can become burdensome. In the extreme, the storage dedicated to them is some fraction of the executable size, I'd guess 25-50%. Imagine, for example, a parser generator which ODR-uses every value of the state variable. Anyway, I'm not the first or the last to ask you to stop telling question askers what to do…
-
rustyx over 7 years@Potatoswatter - integer overflow is UB. The compiler is not required to execute that at run time.
-
Potatoswatter over 7 years@RustyX The point is that the compiler is required to refuse to evaluate it at compile time, so if you add
constexpr
there will be a hard error. -
Wormer almost 6 yearsIsn't it enough to say "constexpr int ret = (123);" to make 123 become an expression?
-
Potatoswatter almost 6 years@Wormer
123
alone is an expression in the first place, and it has the prvalue category that we want. The problem is binding it to a reusable namefoo
such thatfoo
has the same category as123
. -
Wormer almost 6 yearsMy question is about provided code of function value(). Why do we need 120+3 there? can't we change that to (123)? or why we can't put just 123? Sorry for misunderstanding.
-
Potatoswatter almost 6 years@Wormer Yes, it doesn’t matter what goes there. I was trying to insert a calculation into the example but it’s a little confusing.
-
Wormer almost 6 yearsI'm feeling so terrible thinking how I would write something like this to only give a simple name for my compilation time constant. Oh why we have constexpr and all this fancy template stuff in C++ and don't have a thing for such a simple notion.
-
Potatoswatter almost 6 years@Wormer Because the difference is usually insignificant. This only matters (1) when the compile-time constant should not be allocated (e.g. very large tables, or systems where the executable needs to be very small, such as low-cost embedded or virus development) and (2) in cases where allocation leads to ODR violation. For (1) an
[[attribute]]
would be suitable and for (2) the committee would like a better ODR (in the same way that it would like a unicorn; the modules TS occupies its efforts in the design space but only makes the problem worse).