Стандартный способ принудительного применения constexpr для частей сложного выражения в C ?

#c #constexpr #compile-time

#c #constexpr #время компиляции

Вопрос:

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

Например, если у нас есть f(/*some args*/) функция constexpr , она может быть вычислена как во время компиляции, так и во время выполнения. Но если мы хотим принудительно использовать его во время компиляции, один из способов — присвоить его значение constexpr константе:

 constexpr auto x = f(/*some args*/);
 

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

 int x = 123;
int y = x   456 * f(/*some args*/);
 

В приведенном выше выражении одним из способов принудительного вычисления времени компиляции f является использование std::integral_constant :

 int x = 123;
int y = x   456 * std::integral_constant<int, f(/*some args*/)>::value;
 

Но, может быть, есть какой-то более короткий способ сделать это в стандартном C ? Например. может быть, есть какой-то оператор constexpr: 456 * constexpr(f(/*some args*/)) ?

Самая короткая форма, которую я понял:

 template <auto V>
constexpr inline auto ce = V;


int y = x   456 * ce<f(/*some args*/)>;
 

Это решение хорошее, но, может быть ce<...> , внутри стандартной библиотеки C уже есть константа, похожая на шаблонную? Чтобы я не изобретал новые классы / функции, если есть какие-то стандартные. Потому что текущий стандартный класс std::integral_constant требует указания типа в качестве первого аргумента, а также нуждается ::value , что не очень короткий способ записи.

Почему я беспокоюсь о принудительном применении времени компиляции? Из-за следующего кода:

Попробуйте онлайн!

 struct S {
    consteval bool has42() const {
        return i == 42;
    }
    int i = 0;
};

template <auto V>
constexpr inline auto ce = V;

int main() {
    constexpr S s;
    int z = 10;
    auto x = z   int(ce<s.has42()>); // OK
    auto y = z   int(s.has42()); // Compile error
}
 

Приведенный выше код отлично работает в CLang, но в последней строке MSVC with auto y.... не компилируется с ошибкой error C7595: 'S::has42': call to immediate function is not a constant expression . Строка с auto x..... корректно компилируется как на CLang, так и на MSVC, благодаря использованию ce<...> которого выполняется принудительное вычисление времени компиляции.

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

Комментарии:

1. Это может зависеть от используемой вами версии компилятора. Возможно, некоторые функции C еще не реализованы.

2. @U.W. Я действительно хотел узнать о текущем существующем стандартном способе сделать это. Под стандартом я подразумеваю, что это указано в официальном стандарте ISO C . В настоящее время, если у какого-то конкретного компилятора есть какой-то особый нестандартный способ сделать это, меня это не очень интересует. Другими словами, мне нужен какой-то общий способ, который реализован во всех известных последних компиляторах, по крайней мере, в GCC / CLang / MSVC.