为什么函数指针可以是 `constexpr`?

Why can function pointers be `constexpr`?

编译器如何在程序执行前知道平方根在内存中的什么位置?我以为每次执行程序时地址都会不同,但这有效:

constexpr double(*fp)(double) = &sqrt;
cout << fp(5.0);

是不是因为地址是相对于内存中的另一个地址?我不这么认为,因为 fp 的值很大:0x720E1B94.

在编译时,编译器不知道sqrt的地址。但是,您不能在编译时使用允许您访问该指针的 地址 的 constexpr 函数指针执行任何操作。因此,编译时的函数指针可以被视为不透明值。

并且由于您无法在初始化后更改 constexpr 变量,因此每个 constexpr 函数指针都可以归结为特定函数的位置。

如果你做了这样的事情:

using fptr = float(*)(float);

constexpr fptr get_func(int x)
{
  return x == 3 ? &sqrtf : &sinf;
}

constexpr fptr ptr = get_func(12);

对于任何特定的编译时值,编译器可以准确地检测到哪个函数 get_func 将 return。所以 get_func(12) 减少到 &sinf。因此,无论 &sinf 编译成什么,get_func(12) 都会编译成什么。

How does the compiler know where in memory the square root will be before the program is executed?

工具链决定将函数放在何处。

Is it because the address is relative to another address in memory?

如果生成的程序是 relocatable or position independent 那么是的,就是这种情况。如果程序两者都不是,那么地址甚至可以是绝对地址。

Why would the exact same memory spots be available next time the program is run?

因为内存space是virtual.

地址值由链接器分配,因此编译器不知道确切的地址值。

cout << fp(5.0); 

这是可行的,因为它是在 运行 时在解析确切地址后进行评估的。

通常,您不能使用 constexpr 指针的实际值(地址),因为它在编译时是未知的。

Bjarne Stroustrup 的 C++ 编程语言第 4 版提到:

10.4.5 Address Constant Expressions

The address of a statically allocated object (§6.4.2), 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

很简单。

考虑一下编译器如何知道要在这段代码中调用的地址:

puts("hey!");

编译器不知道 puts 的位置,它也没有为它添加运行时查找(这对性能来说相当糟糕,尽管它实际上是 类 需要做)。在运行时拥有不同版本动态库的可能性(更不用说地址 space 布局随机化,即使它是完全相同的库文件)确保构建时工具链链接器也不知道它。

因此,当它启动已编译的二进制程序时,由 dynamic linker 来固定地址。这叫做重定位.

您的 constexpr 发生了完全相同的事情:编译器将使用此地址的代码中的每个位置添加到 重定位 table,然后是动态链接器每次程序启动时都会执行它的工作。