14

Let's say I have a magic number I want to get rid off...

//whatever.cpp

for (int i = 0; i < 42; i++)
{
    //...
}

Reasonably I could kill it in two ways:

Either with const int SOMETHING_SOMETHING_MEANING_OF_LIFE = 42
or with constexpr int SOMETHING_SOMETHING_MEANING_OF_LIFE = 42
in the source .cpp file.

Is there any meaningful difference between the two in this case (I recall the compiler deducing that - in either case - that the value does not change and thus the 42 is actually hardcoded in the resulting loop/unrolled loop/whatever machine-code) or does it come down to personal taste?

In a related issue: what if the magic number (and thus the thing that replaces it) were declared in a header (.h) file instead of a source (.ccp) file - would that change things (and if so, how)?

CharonX
  • 2,130
  • 11
  • 33
  • constant expressions are evaluated at compile time. constants are run time values that can only be assigned to once. Rather than relying on the compiler optimization level to achieve inlining you can use a pre-processor macro. `#define MEANING_OF_LIFE 42` for a simple use case such as this. – Avin Kavish Jul 09 '19 at 10:13
  • 3
    @UniqIdentifierAssignedAtBirth - Please post answers in the answer section. Thank you. – StoryTeller - Unslander Monica Jul 09 '19 at 10:19
  • 5
    @StoryTeller So I can down vote for recommending to use macros ;). – Marek R Jul 09 '19 at 10:22
  • 7
    @MarekR - Not circumventing the site's peer review system is the intent, yes. – StoryTeller - Unslander Monica Jul 09 '19 at 10:23
  • @MarekR What's wrong with macros? That sounds like a C++ opinionated coding style to me – Avin Kavish Jul 09 '19 at 10:32
  • 6
    They do not have a scope, they can lead to unexpected build issues, they can have impact on expression where the are used. They do not have a well defined type. They are C style. – Marek R Jul 09 '19 at 10:34
  • 5
    @UniqIdentifierAssignedAtBirth [they](https://isocpp.org/blog/2019/03/macro-evil-in-cpp-code) [are](http://www.stroustrup.com/bs_faq2.html#macro) [heavily](https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es31-dont-use-macros-for-constants-or-functions) discouraged. Please note that all those links are from authoritative sources: e.g. Bjarne Stroustrup himself, isocpp and CppCoreGuidelines (the community effort). – Tarick Welling Jul 09 '19 at 10:36
  • 3
    Since C++17 the one true constant is a `inline constexpr T` :) – NathanOliver Jul 09 '19 at 12:27

2 Answers2

18

const int can be used as part of a constant expression only if it was initialized from one, but it doesn't give you the guarantee that it was.

const int i = 42; // OK, usable in a constant expression
int j = 42;
const int k = j;  // OK, not usable in a constant expression

constexpr int guarantees that the initializer for your variable is a constant expression, otherwise your program will not compile.

constexpr int i = 42; // OK, usable in a constant expression
int j = 42;
constexpr int k = j;  // Compile-time error, 'j' is not a constant expression

Therefore, if you want to ensure that your initializer is indeed a constant expression, constexpr is a better choice.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Also, it can only be used as constant expression *after* the initialisation. That is, if you have a forward declaration without initialiser, it cannot be used as constant expression even if the definition does initialise with a constant expression later (until after that definition) . – eerorika Jul 09 '19 at 11:05
  • 2
    Thank you very for the excellent answer - this is one of the cases where I wish I could accept two answers *sigh* – CharonX Jul 09 '19 at 13:05
12

Is there any meaningful difference between the two in this case (I recall the compilering deducing that - in either case - the value does not change and actually hardcodes the 42 in the resulting loop/unrolled loop/whatever code) or does it come down to personal taste?

There won't be any difference in codegen in the case you have shown.

However, the difference is that a constexpr variable guarantees that the value is known at compile-time. See VittorioRomeo's answer.

It is also good to write constexpr if it is truly a compile-time value, for documentation purposes: when someone reads your code and sees constexpr, they automatically know it is a truly fixed value. This is important in the case the initialization is non-trivial (e.g. a call to a function).

You can also see constexpr variables as the true replacement for C macros that contained literals (e.g. #define FOO 123).

Finally, remember that constexpr implies const.

In a related issue: what if the magic number (and the thing that replaces it) were declared in a header file instead of a .ccp file - would that change things?

No. However, if you are declaring global variables in a header file, you probably want to use inline (available in C++17) on top of constexpr, so that you only have a single entity in the program, which is an advantage to avoid ODR issues and possibly save memory and initialization time.

See Should `const` and `constexpr` variables in headers be `inline` to prevent ODR violations? for more information.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Acorn
  • 24,970
  • 5
  • 40
  • 69