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

Coo*_*ter 24 c++ function-pointers constexpr

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

constexpr double(*fp)(double) = &sqrt;
cout << fp(5.0);
Run Code Online (Sandbox Code Playgroud)

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

Nic*_*las 25

在编译时,编译器不知道地址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);
Run Code Online (Sandbox Code Playgroud)

编译器可以准确地检测哪个函数get_func将返回任何特定的编译时间值.所以get_func(12)减少到&sinf.所以&sinf编译到的任何东西都是get_func(12)编译到的.

  • @fishinear 编译器没有_知道实际值。它只需要知道它是一个在执行过程中不能改变的常量值。这允许它直接引用生成的可执行文件中的 `sqrtf` 或 `sinf` 符号:它允许消除常量参数的分支和调用指针。 (3认同)
  • @Deduplicator:两者都有。 (2认同)

SHH*_*SHH 17

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

cout << fp(5.0); 
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为它在解析完确的地址后在运行时进行评估.

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

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

10.4.5地址常量表达式

静态分配的对象(第6.4.2节)的地址(例如全局变量)是常量.但是,它的值由链接器而不是编译器分配,因此编译器无法知道此类地址常量的值.这限制了指针和引用类型的常量表达式的范围.例如:

   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)

  • 请注意,`constexpr const char*p3 = p1 + 2;`就我所知而言是好的.`p1 + 2`是一个值,表示具有静态存储持续时间的子对象的地址,是一个有效的常量表达式; 在这种情况下,不需要完整的对象.您不能将它用作非类型模板参数,但这是一个不同的故事.我在标准中找不到任何会使这种结构形成不良的东西; 这适用于C++ 11及更高版本.我测试的所有编译器都接受它.另请参见[此答案](http://stackoverflow.com/a/17660391/4326278). (2认同)

eer*_*ika 6

在程序执行之前,编译器如何知道平方根在内存中的位置?

工具链可以决定它放置功能的位置.

是因为地址是相对于内存中的另一个地址吗?

如果生成的程序要么是可重定位的,要么是位置独立的,那么就是这种情况.如果程序既不是,那么地址甚至可以是绝对的.

为什么下次运行程序时可以使用完全相同的内存点?

因为内存空间是虚拟的.

  • 这个空间是虚拟的,但今天也常用[ASLR](https://en.wikipedia.org/wiki/Address_space_layout_randomization). (3认同)

hyd*_*yde 6

这很简单。

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

puts("hey!");
Run Code Online (Sandbox Code Playgroud)

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

所以当它启动编译的二进制程序时,由动态链接器来修复地址。这称为重定位

完全相同的事情发生在你的constexpr:编译器使用这个地址将代码中的每个地方添加到重定位表中,然后动态链接器在每次程序启动时完成它的工作。