Позволит ли consteval использовать static_assert для аргументов функции?
В настоящее время вы не можете использовать static_assert
проверить параметры constexpr
функция, даже если все вызовы к ней действительно constexpr
, Это имеет смысл, потому что компилятор все еще должен создать экземпляр этой функции без constexpr в случае, если какой-то другой модуль попытается вызвать его. К сожалению, это так, даже если функция static
или в анонимном пространстве имен.
C++ 20, однако, введет новое ключевое слово consteval
который как constexpr
но это не позволяет вызывать функцию не-constexpr способом. В этом случае компилятор может точно знать, что параметры функции всегда будут известны во время компиляции. Следовательно, в теории должно быть возможно проверить их static_assert
,
Вопрос в том, позволяет ли это стандарт?
Пример:
#include <iostream>
consteval char operator""_bchar(const char text[], const size_t length)
{
static_assert(length == 8, "Binary char has to have 8 digits!"); // <-- This is currently not possible.
uint8_t byte = 0;
for (size_t i = 0; i != length; ++i)
{
byte <<= 1;
byte |= text[i] == '1' ? 0b00000001 : 0b00000000;
}
return byte;
}
int main()
{
std::cout << "01000001"_bchar << std::endl;
return 0;
}
Я спрашиваю, потому что я собираюсь написать некоторые пользовательские литералы (более сложные, чем в примере). У меня есть возможность использовать расширения компилятора, чтобы справиться с проверкой или немного подождать обновления компилятора и написать полностью совместимый со стандартом код.
3 ответа
Позволит ли consteval использовать static_assert для аргументов функции?
Нет. Аргументы функций никогда не были и не будут использоваться в качестве константных выражений.
Есть разница между чем-то, что постоянно оценивается и может использоваться как константное выражение. consteval
гарантирует, что мы находимся в постоянном контексте оценки, но это также не заставляет все становиться постоянными выражениями.
Чтобы аргументы функции можно было использовать в качестве константных выражений, вам нужно сделать все неявным образом шаблоном:
template <int> struct X { };
consteval auto foo(int i) {
static_assert(i > 10); // in order to allow this...
return X<i>{}; // ... you'd have to allow this too
}
И сейчас foo(20)
а также foo(30)
вернуть разные типы. Это шаблон.
Важную справочную информацию для понимания того, почему это фундаментальное и неотъемлемое ограничение, можно найти в " Переводе и оценке Эндрю Саттона : ментальная модель для метапрограммирования во время компиляции":
Наличие ментальной модели оценки во время компиляции, которая физически отделяет ее от процесса перевода, было для меня чрезвычайно полезным. В частности, это помогло мне понять, что невозможно (например, создание шаблона во время оценки). Это помогает сократить пространство дизайна для других больших и сложных языковых функций. Надеюсь, другие найдут эту заметку полезной.
С участием static_assert
в частности, вы можете добавить обходной путь только для того, чтобы вызвать сбой компиляции. Это просто добавление всего, что не может быть использовано во время постоянной оценки. Подобно:
#define CONSTEVAL_STATIC_ASSERT(c, msg) do { if (!(c)) throw msg; } while(false)
как в:
consteval char operator""_bchar(const char text[], const size_t length)
{
CONSTEVAL_STATIC_ASSERT(length == 8, "Binary char has to have 8 digits!");
// ...
}
Я согласен с комментаторами выше - это невозможно использоватьstatic_assert()
с аргументами функции, но все еще возможно вызвать ошибку компиляции в функции consteval при условии аргумента. Т.е. получить тот же эффектstatic_assert
предназначен для.
consteval char operator""_bchar(const char text[], size_t length)
{
//static_assert(length == 8, "Binary char has to have 8 digits!");
length /= (length == 8); // Binary char has to have 8 digits!
}
Хитрость в том,(length != 8)
запускает деление на ноль, которое не является постоянным выражением.
Ошибка компиляции будет выглядеть так (gcc-11):
test.cpp: In function ‘int main()’:
test.cpp:110:18: in ‘constexpr’ expansion of ‘operator""_bchar(((const char*)"12345"), 5)’
test.cpp:104:12: error: ‘(5 / 0)’ is not a constant expression
104 | length /= (length == 8); // Binary char has to have 8 digits!
| ~~~~~~~^~~~~~~~~~~~~~~~
!!!ВНИМАНИЕ,ВНИМАНИЕ,ВНИМАНИЕ!!!: это работает вconsteval
ТОЛЬКО функции . Если используется вconstexpr
функции, ваша программа будет убита с ошибкой деления на ноль . Использоватьassert()
вместо этого или выдать исключение.
Я изменил технику из ответа @Barry, чтобы она работала во всех случаях.consteval
,constexpr
и обычные функции.
#include <assert.h>
#include <type_traits>
#define constexpr_assert(expression) do{if(std::is_constant_evaluated()){if(!(expression))throw 1;}else{assert(!!(expression));}}while(0)
Когда поддержка C++23 станет лучше, можно будет использоватьif consteval
чтобы немного улучшить его:
#include <assert.h>
#define constexpr_assert(expression) do{if consteval{if(!(expression))throw 1;}else{assert(!!(expression));}}while(0)