How does the compiler know where in memory the square root will be before the program is executed? I thought the address would be different everytime the program is executed, bu
At compile time, the compiler doesn't know the address of sqrt
. However, you cannot do anything at compile time with a constexpr function pointer that would allow you to access that pointer's address. Therefore, a function pointer at compile time can be treated as an opaque value.
And since you can't change a constexpr variable after it has been initialized, every constexpr function pointer can be boiled down to the location of a specific function.
If you did something like this:
using fptr = float(*)(float);
constexpr fptr get_func(int x)
{
return x == 3 ? &sqrtf : &sinf;
}
constexpr fptr ptr = get_func(12);
The compiler can detect exactly which function get_func
will return for any particular compile time value. So get_func(12)
reduces down to &sinf
. So whatever &sinf
would compile to is exactly what get_func(12)
would compile to.
It's simple.
Consider how compiler knows the address to call in this code:
puts("hey!");
Compiler has no idea of the location of puts
, and it also doesn't add a runtime lookup for it (that'd be rather bad for performance, though it is actually what virtual methods of classes need to do). The possibility of having a different version of dynamic library at runtime (not to mention address space layout randomization even if it is the exact same library file) makes sure the build time toolchain linker doesn't know it either.
So it's up to the dynamic linker to fix the address, when it starts the compiled binary program. This is called relocation.
Exact same thing happens with your constexpr
: compiler adds every place in the code using this address to the relocation table, and then dynamic linker does its job every time the program starts.
How does the compiler know where in memory the square root will be before the program is executed?
The tool chain gets to decide where it puts the functions.
Is it because the address is relative to another address in memory?
If the produced program is either relocatable or position independent then yes, that's the case. If the program is neither, then the address can even be absolute.
Why would the exact same memory spots be available next time the program is run?
Because the memory space is virtual.
Address value is assigned by a linker, so the compiler does not know the exact address value.
cout << fp(5.0);
This works because it is evaluated at run-time after exact address has been resolved.
In general, you cannot use the actual value (address) of constexpr
pointer because it is not known at compile-time.
Bjarne Stroustrup's C++ Programming language 4th edition mentions:
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