Функции constexpr не вызываются во время компиляции, если результат игнорируется
Я исследовал некоторые довольно странные результаты покрытия кода constexpr
функции (вызовы времени компиляции не могут быть инструментированы с помощью инструмента покрытия кода, который я использую), и заметил, что некоторые constexpr
функции вызывались как функции времени выполнения, если результаты вызова функции не сохранялись.
Похоже, что для constexpr
функции или методы, если вы сохраняете результаты вызова (либо в переменной времени выполнения [курсив!!!], либо в constexpr
переменная), вызов является вызовом времени компиляции (пока параметры находятся во время компиляции). Если вы проигнорируете результаты, вызов будет вызовом времени выполнения. Это не имеет ничего общего с моим инструментом покрытия кода; в приведенном ниже простом примере поведение кажется повторяемым.
Вы можете утверждать, что с тех пор constexpr
функции не могут иметь побочных эффектов, не имеет значения, что делает компилятор, если вы не возвращаете / не используете результат. Я бы подумал, что для эффективности компилятор все равно сделает все, что можетconstexpr
, но это ни здесь, ни там... Мне интересно, является ли это вообще определенным поведением.
Это портативный способ гарантировать constexpr
функции будут вызываться как время выполнения??? Для этого не так много вариантов использования, но одно: если вы хотите "принять во внимание тесты, которые были вызваныconstexpr
functions"в покрытии кода, просто вызывая ту же функцию в конце модульного теста и игнорируя результат, чтобы они были инструментами.
Есть и другие способы заставить функцию вызываться как среда выполнения, я знаю, мне больше всего любопытно, что здесь происходит. Когда я впервые увидел это, это было очень неожиданно. Если это не переносной, я, вероятно, просто позвоню своемуconstexpr
методы через объект среды выполнения (который, кажется, помогает даже для статических методов) или через указатель функции времени выполнения.
См. Пример ниже. Живая демонстрация: https://onlinegdb.com/rJao0RNGP
// Modified from https://stackru.com/a/40410624/12854372
extern bool no_symbol;
struct ContextIsConstexpr {
size_t s;
constexpr ContextIsConstexpr() : s(1) {}
constexpr void operator()() {
auto ignore = s ? 1 : no_symbol;
}
};
constexpr bool tryIgnoringResult()
{
ContextIsConstexpr()();
return true;
}
constexpr void thereIsNoResult() {
ContextIsConstexpr()();
}
int main()
{
constexpr auto result1 = tryIgnoringResult(); // OK, compile-time
auto result2 = tryIgnoringResult(); // OK, compile-time
// tryIgnoringResult(); // Unexpected, runtime!
// thereIsNoResult(); // Unexpected, runtime!
}
1 ответ
Это может сбивать с толку, но функции constexpr следует вызывать во время компиляции только в контексте constexpr (присвоение переменной constexpr, использование для размера массива или параметра шаблона, ...).
В обычном контексте функции вызываются во время выполнения. Оптимизатор может разрешить эту функцию во время компиляции (как и для любых других функций, следующих правилу "как если бы").constexpr
действительно хороший намек на оптимизацию.
Вы можете возразить, что, поскольку функции constexpr не могут иметь побочных эффектов
Они могут иметь побочные эффекты, см. Следующий действительный пример:
constexpr int f(int i)
{
if (i == 0) return 0;
std::cout << i << std::endl;
return i;
}
int main()
{
[[maybe_unused]] constexpr int zero = f(0); // Compile time
f(42); // runtime
}