Можно ли узнать, когда constexpr действительно является constexpr?

Поскольку расширенные версии constexpr (я думаю, из С++ 14), вы можете объявлять функции constexpr, которые могут быть использованы как "реальные" constexpr, то есть код, выполняемый во время компиляции, или может вести себя как встроенные функции. Итак, когда у вас может быть эта программа:

#include <iostream>

constexpr int foo(const int s) {
  return s + 4;
}

int main()
{
    std::cout << foo(3) << std::endl;
    const int bar = 3;
    std::cout << foo(bar) << std::endl;
    constexpr int a = 3;
    std::cout << foo(a) << std::endl;

    return 0;
}

Конечно, результат:

7
7
7

пока что так хорошо. Поэтому мой вопрос: есть ли способ (возможно, стандартный) знать в foo (const int s), если функция запускается во время компиляции или во время выполнения?

EDIT: Также возможно узнать во время выполнения, если функция была оценена во время компиляции?

Ответ 1

Указанная техника работает, но поскольку она использует static_assert, она не является дружественной sfinae. Лучший способ (теоретически, вы поймете, что я имею в виду), чтобы проверить, является ли функция noexcept. Зачем? Потому что постоянные выражения всегда не исключают, даже если функции не отмечены как таковые. Итак, рассмотрим следующий код:

template <class T>
constexpr void test_helper(T&& t) {}

#define IS_CONSTEXPR(...) noexcept(test_helper(__VA_ARGS__))

test_helper - constexpr, поэтому оно будет постоянным выражением, пока его аргумент. Если это постоянное выражение, оно будет noexcept, но в противном случае оно не будет (поскольку оно не помечено как таковое).

Итак, теперь определим это:

double bar(double x) { return x; }

constexpr double foo(double x, bool b) {
    if (b) return x; 
    else return bar(x);
}

foo является только noexcept, если x является постоянным выражением, а b является истинным; если логическое значение false, то мы вызываем функцию non constexpr, разрушая нашу constexpr-ness. Итак, давайте протестируем это:

double d = 0.0;

constexpr auto x = IS_CONSTEXPR(foo(3.0, true));
constexpr auto y = IS_CONSTEXPR(foo(3.0, false));
constexpr auto z = IS_CONSTEXPR(foo(d, true));

std::cerr << x << y << z;

Он компилируется, здорово! Это дает нам время компиляции booleans (а не сбои компиляции), которое может использоваться, например, для sfinae.

Улов? Ну, clang имеет многолетнюю ошибку, и не справляется с этим правильно. gcc однако, делает. Пример: http://coliru.stacked-crooked.com/a/e7b037932c358149. Он печатает "100", как и должно быть.

Ответ 2

С++ 20 представляет is_constant_evaluated, определенный в заголовке <type_traits>, который решает эту проблему.

constexpr int foo(int s)
{
    if (std::is_constant_evaluated()) // note: not "if constexpr"
        /* evaluated at compile time */;
    else
        /* evaluated at run time */;
}

Обратите внимание, что здесь используется if вместо if constexpr. Если вы используете if constexpr, то условие должно оцениваться во время компиляции, поэтому is_constant_evaluated всегда возвращает true, делая тест бесполезным.

Ответ 3

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

#include <iostream>

constexpr int foo(const int s) {
  return s + 4;
}

int main()
{
    std::cout << foo(3) << std::endl;
    const int bar = 3;
    std::cout << foo(bar) << std::endl;
    constexpr int a = 3;
    std::cout << foo(a) << std::endl;

    static_assert(foo(3) == 7, "Literal failed");
    static_assert(foo(bar) == 7, "const int failed");
    static_assert(foo(a) == 7, "constexpr int failed");
    return 0;
}

clang++ -std=c++14 so1.cpp компилируется для меня отлично, показывая, что все работает так, как ожидалось.

Ответ 4

Внутри функции constexpr вы не можете определить, выполняете ли вы оценку в контексте constexpr. Было несколько предложений по добавлению этой функциональности. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0595r2.html успешно выполнен и может появиться в , за исключением катастрофы, такой как .


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

Предполагая, что ваше выражение constexpr возвращает непустой интеграл или тип указателя (включая указатель на функцию):

#define CONSTEXPR_EVAL(...) \
  std::integral_constant< \
    std::decay_t<decltype(__VA_ARGS__)>, \
    __VA_ARGS__ \
  >::value

тогда CONSTEXPR_EVAL( bar(foo, true) ) не удастся скомпилировать, если bar(foo, true) не может быть оценен во время компиляции, и если он может быть оценен во время компиляции, он возвращает это значение.

Другие приемы, включающие noexcept (функция, вычисляемая во время компиляции - noexcept), могут работать (см. @NirFriedman answer).

Ответ 5

Извините, что испортил вечеринку, но, безусловно, нет стандартного способа сделать это. В соответствии с правилом as-if компилятор может испускать код, который вычисляет результат во время выполнения даже в тех случаях, когда он уже был вынужден вычислить его во время компиляции в другом контексте. Все, что можно сделать во время компиляции, может быть сделано снова во время выполнения, правильно? И расчет уже доказал, что он не бросается.

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

Конечно, обычно нет причин повторять вычисления во время выполнения, которые могут быть выполнены или даже были выполнены во время компиляции, но вопрос состоял в том, чтобы сказать, использует ли компилятор предварительно вычисляемый результат, и нет 't one.

Теперь вы сказали, что, возможно, стандартный, так что, по сути, ваш лучший выбор - это, вероятно, тестирование одного из предоставленных решений с вашим компилятором, и надеемся, что он будет вести себя последовательно. (Или прочитать исходный код, если он открытый/общедоступный источник, и вы настолько склонны.)