2

I'm reading C++ Templates: The Complete Guide, chapter 23. Metaprogramming. At the end, it describes the difference in using enumeration values versus static constants in metaprogramming. Consider the following two implementations of calculating Nth power of 3:

Enumeration implementation:

// primary template to compute 3 to the Nth
template<int N>
struct Pow3 {
   enum { value = 3 * Pow3<N-1>::value };
};

// full specialization to end the recursion
template<>
struct Pow3<0> {
   enum { value = 1 };
};

Static constant implementation:

// primary template to compute 3 to the Nth
template<int N>
struct Pow3 {
   static int const value = 3 * Pow3<N-1>::value;
};
// full specialization to end the recursion
template<>
struct Pow3<0> {
   static int const value = 1;
};

Right after that it says there is a drawback to the latter version. If we got a function void foo(int const&); and pass it the result of metaprogram: foo(Pow3<7>::value); the book says:

A compiler must pass the address of Pow3<7>::value, and that forces the compiler to instantiate and allocate the definition for the static member. As a result, the computation is no longer limited to a pure “compile-time” effect. Enumeration values aren’t lvalues (i.e., they don’t have an address). So, when we pass them by reference, no static memory is used. It’s almost exactly as if you passed the computed value as a literal.

I don't understand their explanation at all to be honest. I thought in the static constant version we evaluate the result at compile time, but the actual referencing happen in run-time because we need to pass the adress. But then I don't see much difference in the former case (enum implementation) because we should have temporary materialization there (prvalue -> xvalue conversion) and since temporary objects also have the adress my thinking process fails. Also it says "no static memory is used" which I don't understand as well since static memory should refer to allocation that happen at compile-time.

Could someone explain this whole thing in greater detail?

2
  • A static member has a link-time effect: You'll get a symbol for the variable, and the linker has to deal with it. Temporaries on the other hand are not relevant to the linker at all. Commented Apr 29, 2021 at 13:12
  • From another point of view, objects typically have distinct addresses during their lifetime. Static variables have static storage duration (= lifetime of process/library). Therefore, their lifetime is maintained by .. static entries in the executable (if they're not dropped by optimization). Commented Apr 29, 2021 at 13:16

2 Answers 2

1

(enum implementation) because we should have temporary materialization there (prvalue -> xvalue conversion) and since temporary objects also have the address

they do, but the temporary xvalue only exists for the duration of the function call. It's identical to passing the function an integer literal. So, an addressable object exists temporarily, at runtime, with automatic scope.

Conversely,

... that forces the compiler to instantiate and allocate the definition for the static member. As a result, the computation is no longer limited to a pure “compile-time” effect.

This static const int is an object with static storage duration. There's no temporary materialization necessary, it's an object, it existed when your program started, and you're taking a reference to it.

If you write multiple .cpp files including the header with Pow3, and they all call foo(Pow3<3>):

  • the enum version will emit something like foo(27) in each translation unit, with three unrelated temporary xvalues
  • the static version will emit a global object equivalent to const int Pow3<3>::value = 27; and each translation unit will refer to (ie, take a reference to) the same object
Sign up to request clarification or add additional context in comments.

2 Comments

So you're saying it's better to use a piece of memory only for a period of time instead of using it during the whole execution ? And that makes perfect sense now. But I'm still confused with the "...no longer limited to a pure 'compile-time' effect." thing since both scenarios involve run-time effect.
It might be fairer to say that the prvalue version affects only the executable code, while the static one affects both code and data. I'm not really saying it's better either, just different: the most significant effect is probably on the symbol table.
1

I suggest looking at it this way:
There is a difference between a type, an instance of a type, and the values said type can take.

In the first case you are specifying a type (an unnamed enum) with only one possible value value (which is the compile-time constant).
There are no instantiations of the type, so no memory will be used compile-time or runtime.
Every time the code refers to Pow3<N>::value, the compiler will not create an instance of the type but will instead use the constant directly.

In the second case you are instead specifying a variable value of type int, and assigning the compile-time constant to it.
This variable exists, so memory will be used.
Every time the code refers to Pow3<N>::value, the compiler will use said variable.

3 Comments

I believe I get your point and it makes sense to a certain degree. But I don't see you've mentioned referencing anywhere and I'm getting a feel it is necessary as pointed out in the book. If the signature was void foo(int const); would there be a need for allocating ?
@JamesGroon To me the quote seems a bit misleading in the use of the "by reference" term. Non-const references have to point to something existing, so the enum value can only be passed by value or const-reference (creating a temporary instantiation). For void foo(int) the compiler will probably still allocate the value variable, but it might be optimized away in a later step (optimizations are pretty much a wild-card in these cases).
I agree. There is only one thing that bugs me and I've asked the same thing to the fellow named useless. The book said "...no longer limited to a pure 'compile-time' effect.", but I don't see the meaning behind that since the enum version also involve run-time effect. But the effect is desirable since the memory gets free at the end of the function execution instead of when using static const version.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.