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.
};
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 word Opaque
or the other constants you want to use?
Indeed, this is trickier than it might appear.
Just to explicitly restate the requirements:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With