19

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 word Opaque or the other constants you want to use?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Malvineous
  • 25,144
  • 16
  • 116
  • 151
  • Are you sure you're so tight on memory that 4 bytes makes a difference? – Brian Bi Feb 27 '15 at 00:26
  • 1
    Why 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 Feb 27 '15 at 00:32
  • 3
    @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.) – Malvineous Feb 27 '15 at 00:38
  • Yes 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! – Lightness Races in Orbit Feb 27 '15 at 00:55
  • Well ok, I'm using the fields as flags, for example `if (value & SomeFlag) { /* do something */ }`. Rather than say `if (pixel & 2)` I would rather say `if (value & Opaque)` as it's easier to read, but the value of `Opaque` 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. – Malvineous Feb 27 '15 at 01:50
  • My practices haven't been updated for C++11 yet but I just use regular `const int a = 0;` in a namespace, sometimes `static const int a = 0` in a class. These won't use up memory at run-time if not required to exist in memory. – Neil Kirk Feb 27 '15 at 02:15
  • @Malvineous: Ok, done. – einpoklum Nov 03 '16 at 15:33

3 Answers3

14

Indeed, this is trickier than it might appear.

Just to explicitly restate the requirements:

  1. There should be no runtime computation.
  2. 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.

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • 1
    are you saying that simply `return 123 + 3;` is not performed at compile time? – vsoftco Feb 27 '15 at 14:00
  • 1
    @vsoftco Not quite. But `constexpr` variables are *always guaranteed* to be treated as constant expressions whereas `constexpr` 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 were `int ret = INT_MAX + 3;`, that would be different because there would be signed integer overflow. Using `constexpr` very liberally helps prevent such gotchas. – Potatoswatter Feb 28 '15 at 04:39
  • @Potatoswatter - integer overflow is UB. The compiler is not required to execute that at run time. – rustyx Sep 15 '16 at 20:13
  • @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. – Potatoswatter Sep 16 '16 at 03:30
  • Isn't it enough to say "constexpr int ret = (123);" to make 123 become an expression? – Wormer Jul 26 '18 at 17:58
  • 1
    @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 name `foo` such that `foo` has the same category as `123`. – Potatoswatter Jul 26 '18 at 19:44
  • My 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. – Wormer Jul 26 '18 at 21:54
  • 1
    @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. – Potatoswatter Jul 27 '18 at 02:04
  • I'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. – Wormer Jul 27 '18 at 20:41
  • @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). – Potatoswatter Jul 27 '18 at 20:48
3

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.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
-1

You can't go wrong with:

static constexpr const unsigned int Value = 123;

Honestly, though, try not to care about this. Like, really try.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Doesn't really answer the question though. Why use `static` at all? How does this work with `const char*` values (the code in my question was pseudocode and won't compile) – Malvineous Feb 27 '15 at 00:42
  • Yes, it tells me `error: expected ‘)’ before ‘purpose’` in the `metadata()` function. – Malvineous Feb 27 '15 at 01:35
  • @Malvineous string literal concatenation applies to, well, string literals. – T.C. Feb 27 '15 at 02:00
  • @Malvineous If you want the translation phase 6 string literal concatenation, then a macro is pretty much your only choice. – T.C. Feb 27 '15 at 02:06
  • 4
    You 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. – Potatoswatter Feb 27 '15 at 02:10
  • 9
    It's hard not to care about it when C++ has 53910 ways of defining constants. – Neil Kirk Feb 27 '15 at 02:12
  • @LightnessRacesinOrbit do you need the `const`? I thought `constexpr` implies `const` when declaring an object. – vsoftco Feb 27 '15 at 05:11
  • @vsoftco `constexpr` implies both `const` and `static`. The `static` part is dangerous, and it's even been deprecated in C. – Potatoswatter Feb 27 '15 at 08:04
  • @vsoftco: True. As of C++14, `constexpr` member functions are not implicitly `const` so, for consistency, I prefer to write it out explicitly for object declarations too. – Lightness Races in Orbit Feb 27 '15 at 10:33
  • @NeilKirk: It's extremely easy not to care whether a 4-byte constant is baked into your executable or optimised out. – Lightness Races in Orbit Feb 27 '15 at 10:34
  • 3
    @LightnessRacesinOrbit It's not if you are a perfectionist with OCD. IT BOTHERS ME. – Neil Kirk Feb 27 '15 at 11:17
  • @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 Feb 28 '15 at 04:41
  • 4
    @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. – Potatoswatter Feb 28 '15 at 04:43
  • @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. – Lightness Races in Orbit Feb 28 '15 at 06:18
  • 3
    @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. ??? – Potatoswatter Feb 28 '15 at 07:13
  • @Potatoswatter: Ehm?! I think you've fundamentally misunderstood something. Can you be more specific, so that we can resolve going forwards. Regards – Lightness Races in Orbit Feb 28 '15 at 07:14
  • 1
    … 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… – Potatoswatter Feb 28 '15 at 07:18