Подтвердить что ты не робот

Почему функции указателей могут быть `constexpr`?

Как компилятор знает, где в памяти будет квадратный корень, прежде чем программа будет выполнена? Я думал, что адрес будет отличаться каждый раз, когда программа выполняется, но это работает:

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

Это потому, что адрес относится к другому адресу в памяти? Я так не думаю, потому что значение fp велико: 0x720E1B94.

4b9b3361

Ответ 1

Как компилятор знает, где в памяти будет квадратный корень, прежде чем программа будет выполнена?

Цепочка инструмента определяет, где она помещает функции.

Это потому, что адрес относится к другому адресу в памяти?

Если созданная программа либо relocatable, либо независимая позиция тогда да, это случай. Если программа не является ни одной, то адрес может быть даже абсолютным.

Почему в следующий раз при запуске программы будут доступны одни и те же точки памяти?

Поскольку пространство памяти virtual.

Ответ 2

Во время компиляции компилятор не знает адрес 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).

Ответ 3

Значение адреса назначается компоновщиком, поэтому компилятор не знает точного значения адреса.

cout << fp(5.0); 

Это работает, потому что он оценивается во время выполнения после того, как точный адрес был разрешен.

В общем случае вы не можете использовать фактическое значение (адрес) указателя constexpr, потому что оно неизвестно во время компиляции.

Bjarne Stroustrup С++ Язык программирования 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

Ответ 4

Это просто.

Рассмотрим, как компилятор знает адрес для вызова в этом коде:

puts("hey!");

Компилятор не имеет понятия о местонахождении puts, а также не добавляет к нему внешний вид (это было бы плохо для производительности, хотя на самом деле это то, что нужно делать виртуальным методам классов), Возможность иметь другую версию динамической библиотеки во время выполнения (не говоря уже о рандомизации размещения в адресном пространстве, даже если это тот же самый файл библиотеки) гарантирует, что компоновщик компоновки времени сборки также не знает об этом.

Таким образом, до динамического компоновщика, чтобы исправить адрес, когда он запускает скомпилированную двоичную программу. Это называется перемещением.

Точно такая же вещь происходит с вашим constexpr: компилятор добавляет каждое место в код, используя этот адрес в таблицу перемещения, а затем динамический компоновщик выполняет свою работу каждый раз, когда запускается программа.