Compile-Time Constants and Variables

Compile-time constants and variables

Compile time constant must be:

  • declared final
  • primitive or String
  • initialized within declaration
  • initialized with constant expression

So private final int x = getX(); is not constant.

To the second question private int y = 10; is not constant (non-final in this case), so optimizer cannot be sure that the value would not change in the future. So it cannot optimize it as good as constant value. The answer is: No, it is not treated the same way as compile time constant.

confused about compile time constants

"Compile Time" or "Compiling" is one part of the process. In C++, there is a feature called Macros that are expanded into C++ code during the Pre-Processor stage. This Pre-Processor stage runs first before the compiler. Depending on the compiler, the next step is to convert the human-readable C++ code to another form that is readable by machines. This really depends on the type of the compiler and other possible arguments passed in during compilation as to what this turns into. For sake of simplicity, let's assume the compiler compiles the C++ to symbols/assembly/machine code. Each of these files is independent, for lack of better term.

The last step is to 'link these together. Includes, forward declarations, and externs are resolved during this stage. The once independent files now 'linked' with sizes of types/classes/structs known at the end of the stage. After all this is done, an executable is formed for which is purely machine language and understood by the computer running the program.

A general rule of thumb for C++ programming is you want to catch as many errors as possible at compile time. Run-time is the span of time for which your program is executing the machine code. This can include heap allocations, dynamic list resizing, etc...

Now, to the crux of your question. What is the difference between a Run-Time constant and Compile Time constant? It is quite simple. Compile Time constants are values known and resolvable at compile time. These values can be determined and calculated and depend on no external user input or file data. See constexpr as a good reference for learning more about compile time constants: ConstExprLink.

const int userScore = 3; // Compile time constant
const int secondUserScore = 5; // Compile time constant
constexpr int CombinedUserScores = ( userScore + secondUserScore ); // Also compile constant because all values are known at compile time

However, there are some constants that cannot be known at compile time. These values can be determined and resolved after your program Run-Time begins. Now, in contrast, imagine you are reading user input or data from a file during Run-Time

std::cout << "Enter your Test Score: ";
const int testScore = 0;
std::cin >> testScore; // User sets an input value during Run-Time

const int userScore { testScore }; // Run-Time constant. No way to know the value at compile time

userScore is now a Run-Time constant that cannot be changed unless you engage in some taboo stuff like const_cast or C style casts. The point here is that the compiler has no way to know what the value of this constant would be until the input is read in. This is often called Data-Driven programming and is a very common paradigm. Also, because the Run-Time const is not determined at compile time, constexpr cannot be used here.

The above is an example of a Run-Time constant. This value cannot be resolved at compile time because the compiler has no idea what the input from the user or file might be. Here is another good reference: CompileTimeConstant Versus RunTimeConstant

Where to define compile-time constants?

In C++17, defining compile-time integer constants is easy.

First, you should decide whether or not the constant should be scoped to a class. If it makes sense to have it as a class member (e.g., it pertains to the concept that the class represents) then make it a class member. Otherwise, don't.

As a class member, write:

class JolloManager {
constexpr static int rounds = 3;
};

That's it. No out-of-line definition is required anymore in C++17.

If it's not going to be a class member, but you want everyone who includes your header to be able to access the value, then write this in the header:

inline constexpr int rounds = 3;

(Technically, the reason to use inline is to avoid ODR violations when the variable is ODR-used by an inline function in multiple translation units.)

If the value is an implementation detail that only one .cpp file needs access to, then write the following in that .cpp file to give it internal linkage (i.e., prevent clashing with names in other translation units):

constexpr int rounds = 3;  // no `inline` this time

Finally, if the constant is only needed by a single function, you can make it local to that function:

void foo() {
constexpr int rounds = 3;
}

how to guarantee initilization of a stack variable with a compile time constant

I think it is better to use consteval function, but if you cannot change it, you can simply use a temporary variable which will surely optimize later:

constexpr int func( int i )
{
return i+2;
}

int main()
{
constexpr int i1 = func(8);
auto i2 = i1;
i2 = 9;
}

Although you may not like this method, I think it works without any problem....

You can also use something like this(Based on StoryTeller idea):

template<typename T> constexpr std::remove_const_t<T> const_eval(T&& res) { return res; }

which support returning references from method too.

Difference between final variables and compile time constant

The problem is, that all case: statements must be ultimate at compile time.
Your first statement is ultimate. a will for 100% be no other value than 5.

final int a = 5;

However, this is not guaranteed for b. What if there would be an if-statement around b?

final int b;
if(something())
b=6;
else
b=5;


Related Topics



Leave a reply



Submit