In Bjarne Stroustrup's book "The C++ Programming Language (4th Edition)" on p. 267 (Section 10.4.5 Address Constant Expressions), he uses a code example where the address of a local variable is set to a constexpr
variable. I thought this looked odd, so I tried running the example with g++ version 7.3.0 and was unable to get the same results. Here is his code example verbatim (although slightly abridged):
extern char glob;
void f(char loc) {
constexpr const char* p0 = &glob; // OK: &glob's is a constant
constexpr const char* p2 = &loc; // OK: &loc is constant in its scope
}
When I run this, I get:
error: ‘(const char*)(& loc)’ is not a constant expression
Is something happening with g++ that I'm not aware of, or is there something more to Bjarne's example?
An earlier printing of Bjarne Stroustrup's book "The C++ Programming Language (4th Edition)" on p. 267 has the error outlined in the OP's question. The current printing and electronic copies have been "corrected" but introduced another error described later. It now refers to the following code:
constexpr const char* p1="asdf";
This is OK because "asdf" is stored in a fixed memory location. In the earlier printing the book errs here:
void f(char loc) {
constexpr const char* p0 = &glob; // OK: &glob's is a constant
constexpr const char* p2 = &loc; // OK: &loc is constant in its scope
}
However, loc
is not in a fixed memory location. it's on the stack and will have varying locations depending on when it is called.
However, the current 4th edition printing has another error. This is the code verbatim from 10.5.4:
int main() {
constexpr const char* p1 = "asdf";
constexpr const char* p2 = p1; // OK
constexpr const char* p3 = p1+2; // error: the compiler does not know the value of p1
}
This is wrong. The compiler/linker does know the value of p1 and can determine the value of p1+2
at link time. It compiles just fine.
It appears that the example from section 10.4.5 provided in my hard-copy of the "The C++ Programming Language (4th Edition)" is incorrect. And so I've concluded that the address of a local variable is not a constexpr
.
The example appears to have been updated in some pdf versions as seen here:
This answer tries to clarify why the address of a local variable can't be constexpr
by analysing an example for the x86-64 architecture.
Consider the following toy function print_addr()
, which displays the address of its local variable local_var
and call itself recursively n
times:
void print_addr(int n) {
int local_var{};
std::cout << n << " " << &local_var << '\n';
if (!n)
return; // base case
print_addr(n-1); // recursive case
}
A call to print_addr(2)
produced the following output on my x86-64 system:
2 0x7ffd89e2cd8c
1 0x7ffd89e2cd5c
0 0x7ffd89e2cd2c
As you can see, the corresponding addresses of local_var
are different for each call to print_addr()
. You can also see that the deeper the function call, the lower the address of the local variable local_var
. This is because the stack grows downwards (i.e., from higher to lower addresses) on the x86-64 platform.
For the output above, the call stack would look like the following on the x86-64 platform:
| . . . |
Highest address ----------------- <-- call to print_addr(2)
| print_addr(2) |
-----------------
| print_addr(1) |
-----------------
| print_addr(0) | <-- base case, end of recursion
Lowest address ----------------- Top of the stack
Each rectangle above represents the stack frame for each call to print_addr()
. The local_var
of each call is located in its corresponding stack frame. Since the local_var
of each call to print_addr()
is located in its own (different) stack frame, the addresses of local_var
differ.
To conclude, since the address of a local variable in a function may not be the same for every call to the function (i.e., each call's stack frame may be located in a different position in memory), the address of such a variable can't be determined at compile time, and therefore can't be qualified as constexpr
.
Just to add to other answers that have pointed out the mistake, C++ standard only allows constexpr pointers to objects of static-storage duration, one past the end of such, or nullptr
. See [expr.const/8] specifically #8.2;
It's worth noting that:
extern
variables, they'll inherently have static-storage duration or thread local-storage duration.Hence this is valid:
#include <string>
extern char glob;
std::string boom = "Haha";
void f(char loc) {
constexpr const char* p1 = &glob;
constexpr std::string* p2 = nullptr;
constexpr std::string* p3 = &boom;
}
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