Как определить, выполняется ли `constexpr` во время компиляции (без проверки вручную) - PullRequest
0 голосов
/ 29 августа 2018

Есть ли стандартный способ узнать, что компилятор делает с constexpr функциями?

(Примечание: для отладки каждая функция constexpr по умолчанию откладывается до времени выполнения. Почему это разумно? Есть ли способ повлиять на это?)

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

Мой текущий «обходной путь» (VC ++) - где-то сломаться, перейти к моей функции constexpr и (попытаться) проверить разборку. Если их там нет, я делаю вывод, что все это было сделано во время компиляции. Но это не на 100% надежно. (Оптимизация и т. Д.) Определён только обратный путь: если я найду разборку (и могу даже сломаться), я знаю, что это НЕ было сделано во время компиляции.

1 Ответ

0 голосов
/ 29 августа 2018

Это невозможно. constexpr не гарантирует встраивание значения, вы можете увидеть этот уровень оптимизации манипуляции здесь: https://godbolt.org/z/dAoiM-

Только с -O2 все встраивается и структура растворяется. Ниже этот компилятор успешно использует оценку времени выполнения даже для кода, используемого в constexpr контексте.

Нет стандартных языковых инструментов, чтобы узнать, применяет ли компилятор определенную оптимизацию. Все сводится к как если бы правило . Если код ведет себя так же, компилятор может сделать с ним что угодно. Единственным исключением являются обязательные RVO и другие RVO (им разрешено изменять наблюдаемое поведение).

Это, как говорится. constexpr - полезный совет. В связанном примере, если удалить спецификаторы constexpr, даже O3 (на последних clang и gcc) не удастся удалить карту.

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

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

#include <iostream>
#include <vector>
using namespace std;

constexpr int f(int el) {
    return el > 0 ? el : throw "error";
}

int main() {
    // constexpr auto r = f(-1); // #1 compiler errors that throw is forbidden in  
                                 // constexpr, so it went into a non-constexpr path
                                 // and failed

    constexpr auto r = f(1);     // #2 fine - has to be interpreted in constexpr context
    cout << f(1) << '\n';        // #3 fine - can be interpreted in both contexts

    try {
        cout << f(-1) << '\n'; // # 4 // throws - i.e. runtime evaluation
    }
    catch (const char* e) {
        cout << e << '\n';
    }
    return 0;
}
...