在Bjarne Stroustrup的书“The C ++ Programming Language(4th Edition)”中。 267(第10.4.5节地址常量表达式),他使用一个代码示例,其中局部变量的地址设置为constexpr
变量。我认为这看起来很奇怪,所以我尝试使用g ++版本7.3.0运行该示例,但无法获得相同的结果。这是他的代码示例逐字(虽然略有删节):
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
}
当我运行这个时,我得到:
error: ‘(const char*)(& loc)’ is not a constant expression
用g ++发生的事情我不知道,还是Bjarne的例子还有什么?
早先印刷了Bjarne Stroustrup的书“The C ++ Programming Language(4th Edition)”。 267有OP问题中列出的错误。当前的打印和电子副本已经“更正”,但引入了后面描述的另一个错误。它现在引用以下代码:
constexpr const char* p1="asdf";
这是可以的,因为“asdf”存储在固定的内存位置。在早期的印刷中,这本书在这里出错了:
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
}
但是,loc
不在固定的内存位置。它位于堆栈上,根据调用的时间而有不同的位置。
但是,目前的第4版打印还有另一个错误。这是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
}
这是错的。编译器/链接器确实知道p1的值,并且可以在链接时确定p1+2
的值。它编译得很好。
这个答案试图通过分析x86-64架构的一个例子来澄清为什么局部变量的地址不能是constexpr
。
考虑以下玩具函数print_addr()
,它显示其局部变量local_var
的地址,并递归调用n
次:
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
}
对print_addr(2)
的调用在我的x86-64系统上产生了以下输出:
2 0x7ffd89e2cd8c
1 0x7ffd89e2cd5c
0 0x7ffd89e2cd2c
如您所见,local_var
的相应地址对于每次调用print_addr()
都是不同的。您还可以看到函数调用越深,局部变量local_var
的地址越低。这是因为堆栈在x86-64平台上向下(即,从较高地址到较低地址)增长。
对于上面的输出,call stack在x86-64平台上看起来如下:
| . . . |
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
上面的每个矩形表示每次调用stack frame时的print_addr()
。每个调用的local_var
位于其相应的堆栈帧中。由于每次调用local_var
的print_addr()
都位于其自己的(不同的)堆栈帧中,因此local_var
的地址不同。
总之,由于函数中局部变量的地址对于函数的每次调用可能都不相同(即,每个调用的堆栈帧可能位于存储器中的不同位置),这样的变量的地址可以'在编译时确定,因此不能被认定为constexpr
。
只是为了添加指出错误的其他答案,C ++标准只允许constexpr指向静态存储持续时间的对象,一个超过这样的结束,或者nullptr
。具体参见[expr.const/8]#8.2;
值得注意的是:
extern
变量的约束,它们本身具有静态存储持续时间或线程本地存储持续时间。因此这是有效的:
#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;
}