C ++ функция constexpr в операторе возврата - PullRequest
0 голосов
/ 14 января 2019

Почему функция constexpr оценивается не во время компиляции, а во время выполнения в операторе возврата основной функции?

попробовал

template<int x>
constexpr int fac() {
    return fac<x - 1>() * x; 
} 

template<>
constexpr int fac<1>() {
    return 1; 
} 

int main() {
    const int x = fac<3>();
    return x;
} 

и результат

main:
        push    rbp
        mov     rbp, rsp
        mov     DWORD PTR [rbp-4], 6
        mov     eax, 6
        pop     rbp
        ret

с gcc 8.2. Но когда я вызываю функцию в операторе возврата

template<int x>
constexpr int fac() {
    return fac<x - 1>() * x; 
} 

template<>
constexpr int fac<1>() {
    return 1; 
} 

int main() {
    return fac<3>();
} 

Я получаю

int fac<1>():
        push    rbp
        mov     rbp, rsp
        mov     eax, 1
        pop     rbp
        ret
main:
        push    rbp
        mov     rbp, rsp
        call    int fac<3>()
        nop
        pop     rbp
        ret
int fac<2>():
        push    rbp
        mov     rbp, rsp
        call    int fac<1>()
        add     eax, eax
        pop     rbp
        ret
int fac<3>():
        push    rbp
        mov     rbp, rsp
        call    int fac<2>()
        mov     edx, eax
        mov     eax, edx
        add     eax, eax
        add     eax, edx
        pop     rbp
        ret

Почему первый код оценивается во время компиляции, а второй - во время выполнения?

Также я попробовал оба фрагмента clang 7.0.0, и они оцениваются во время выполнения. Почему этот недействительный constexpr для clang?

Вся оценка была выполнена в проводнике компилятора Godbolt.

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Хороший ответ StoryTeller, но я думаю, что возможен немного другой вариант.

С constexpr можно выделить три ситуации:

  1. Результат необходим в контексте времени компиляции, таком как размеры массивов. В этом случае аргументы тоже должны быть известны во время компиляции. Оценка составляет , вероятно во время компиляции, и по крайней мере все диагностируемые ошибки будут обнаружены во время компиляции.

  2. Аргументы известны только во время выполнения, и результат не требуется во время компиляции. В этом случае оценка обязательно должна происходить во время выполнения.

  3. Аргументы могут быть доступны во время компиляции, но результат необходим только во время выполнения.

Четвертая комбинация (аргументы, доступные только во время выполнения, результат, необходимый во время компиляции), является ошибкой; компилятор отклонит такой код.

Теперь в случаях 1 и 3 вычисление может выполняться во время компиляции, поскольку все входные данные доступны. Но чтобы упростить вариант 2, компилятор должен иметь возможность создавать версию во время выполнения, и он может решить использовать этот вариант и в других случаях - если это возможно.

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

0 голосов
/ 14 января 2019

Распространенным заблуждением относительно constexpr является то, что оно означает «это будет оценено во время компиляции» 1 .

Это не так. constexpr был введен, чтобы позволить нам написать естественный код, который может создавать константные выражения в контекстах, которые в них нуждаются. Это означает «это должно быть оценено во время компиляции» , что и будет проверять компилятор.

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

Но функция constexpr все еще является обычной функцией. Спецификатор constexpr не означает, что компилятор имеет 2 , чтобы оптимизировать его для хека и делать постоянное свертывание во время компиляции. Лучше не путать это с таким намеком.


1 - Спасибо user463035818 за формулировку.
2 - и consteval это отдельная история :)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...