Address Constant Expressions

Que*_*tin 3 pointers constexpr c++11

I'm delving into address constant expressions while reading "C++ Programming Language 4th edition" book. It has a short paragraph which describes address constant expressions:

The address of a statically allocated object, such as a global variable, is a constant. However, its value is assigned by the linker, rather than the compiler, so the compiler cannot know the value of such an address constant. That limits the range of constant expressions of pointer and reference type. For example:

constexpr const char* p1 = "asdf";
constexpr const char* p2 = p1;  //OK
constexpr const char* p2 = p1+2;  //error: the compiler does not know the value of p1
constexpr char c = p1[2]; //OK, c=='d'; the compiler knows the value pointed to by p1
Run Code Online (Sandbox Code Playgroud)

I have two questions.

  1. This one is rather trivial - since the compiler doesn't know that address of a static object, then how can it evaluate the second statement during compile time? After all, the fact that the compiler doesn't know the value of p1+2, implies that p1 has to be unknown in the first place, right? g++ 4.8.1 with all the rigorous flags turned on accepts all these statements, though.

  2. As exemplified in this topic:

static constexpr int N = 3;
int main()
{
  constexpr const int *NP = &N;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Here, NP is declared as an address constant-expression, i.e. an pointer that is itself a constant expression. (This is possible when the address is generated by applying the address operator to a static/global constant expression.)

如果我们N简单地声明为constwithout ,这也将起作用constexpr。但是,p1必须显式声明 usingconstexpr才能p2成为有效语句。否则我们得到:

错误:“p1”的值在常量表达式中不可用

这是为什么?"asdf"是的const char[],据我所知。

Joh*_*itb 5

N3485 包含关于“地址常量表达式”

地址常量表达式是 ... 指针类型的纯右值核心常量表达式(根据上下文要求进行转换后),其计算结果为具有静态存储持续时间的对象的地址 ....

字符串字面量的第三个字符对象就是这样一个对象(参见 2.14.5 的详细说明),不小于其中的第一个。

请注意,这里没有使用变量,而是使用对象(因此我们可以访问数组元素以及类成员以获取地址常量表达式,前提是数组或类对象具有静态存储持续时间并且访问不违反核心常量表达式的规则)。

从技术上讲,链接器将在目标文件中进行重定位:

constexpr const char *x = "hello";
extern constexpr const char *y = x + 2;
Run Code Online (Sandbox Code Playgroud)

我们将把它编译成一个目标文件,看看它做了什么

[js@HOST1 cpp]$ clang++ -std=c++11 -c clangtest.cpp
[js@HOST1 cpp]$ objdump --reloc ./clangtest.o 

./clangtest.o:     file format elf32-i386

RELOCATION RECORDS FOR [.rodata]:
OFFSET   TYPE              VALUE 
00000000 R_386_32          .L.str


[js@HOST1 cpp]$ objdump -s -j .rodata ./clangtest.o 

./clangtest.o:     file format elf32-i386

Contents of section .rodata:
 0000 02000000                             ....            
[js@HOST1 cpp]$ 
Run Code Online (Sandbox Code Playgroud)

链接器将获取该部分中已有的值,并将其添加到由重定位的“VALUE”属性(在我们的例子中,我们添加2,因此 Clang/LLVM2在 部分中硬编码了 a )。

但是,必须使用 constexpr 显式声明 p1,以便 p2 成为有效语句。

那是因为你依赖于它的价值,而不是它的地址,来保持不变。通常(见下文),您必须事先将其标记为 constexpr,以便此时的编译器可以验证任何以后的读取访问肯定依赖于获取常量。您可能希望按如下方式更改它并查看它的工作情况(我认为因为对于整数和枚举类型的初始化 const 对象有一种特殊情况,您甚至可以p1在 constexpr 上下文中从下面的数组中读取,即使它没有被标记constexpr。但是我的叮当似乎拒绝它)

const char p1[] = "asdf";
constexpr const char *x = p1 + 2; // OK!
constexpr char y = p1[2]; // OK!
Run Code Online (Sandbox Code Playgroud)