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

时间:2016-07-16 15:53:02

标签: c++ function-pointers constexpr

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

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

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

4 个答案:

答案 0 :(得分:24)

在编译时,编译器不知道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将为任何特定的编译时间值返回。因此get_func(12)缩减为&sinf。所以无论&sinf编译到什么,都是get_func(12)编译到的。

答案 1 :(得分:16)

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

cout << fp(5.0); 

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

通常,您不能使用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

答案 2 :(得分:6)

  

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

工具链决定将功能放在何处。

  

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

如果生成的程序是relocatableposition independent,那么是的,就是这种情况。如果程序既不是,那么地址甚至可以是绝对的。

  

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

因为内存空间是virtual

答案 3 :(得分:4)

很简单。

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

puts("hey!");

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

因此,当它启动编译的二进制程序时,由dynamic linker来修复地址。这称为重定位

constexpr完全相同的事情:编译器使用此地址将代码中的每个位置添加到重定位表,然后动态链接器在每次程序启动时执行其工作。