#c #constexpr
#c #constexpr
Вопрос:
Итак, я осматривался и пробовал разные вещи, но я не могу понять, как я мог бы создать некоторую коллекцию строк с помощью constexpr.
То, что я пытаюсь сделать, в основном заключается в следующем, что, очевидно, не компилируется:
constexpr std::vector<std::string> fizzbuzz(){
size_t N = 100;
std::vector<std::string> resu<
result.reserve(N);
for (int i = 0; i < N; i ){
int k = i 1;
if(k % 5 == 0 amp;amp; k % 3 == 0){
result.push_back("FizzBuzz");
}
else if(k % 5 == 0){
result.push_back("Buzz");
}
else if(k % 3 == 0){
result.push_back("Fizz");
}
else{
result.push_back(std::to_string(k));
}
}
return resu<
}
Я уже был бы счастлив, если бы понял, как сделать что-то такое простое, как:
constexpr std::string fizzbuzz(int k){
if(k % 3 == 0) return "Fizz";
else return std::to_string(k);
}
Я считаю, что это всего лишь небольшой шаг к полному решению.
Это не обязательно должно быть std::strings это не обязательно должно быть std::vectors.
О, и чем ниже стандарт C , тем лучше.
Отредактировано, чтобы помочь лучше понять проблему.
Комментарии:
1. Вы можете сделать это в c 20.
2.
std::vector
/std::string
не имеет конструктора constexpr (до C 20)…std::array
иstd::string_view
есть.3. @cigien: даже в C 20, как я понимаю, OP не сможет распечатать (во время выполнения) контейнер constexpr (который должен оставаться в оценке constexpr).
4. @Jarod42 Да, это правильно.
5. о, я как-то упустил суть вопроса, мой плохой
Ответ №1:
std::vector
/ std::string
не имеет constexpr
конструктора до C 20… и даже в C 20 constexpr
распределение не должно исключаться из вычисления constexpr, поэтому его нельзя использовать во время выполнения (для печати).
Я не вижу стандартного способа constexpr преобразовать целое число в представление последовательности символов. std::to_string
, std::to_chars
, std::format
не constexpr
.
Таким образом, вместо однородного контейнера вы могли бы использовать std::tuple
что-то вроде (C 17):
template <std::size_t I>
constexpr auto fizzbuzz_elem()
{
if constexpr (I % 5 == 0 amp;amp; I % 3 == 0) {
return "FizzBuzz";
} else if constexpr (I % 5 == 0) {
return "Buzz";
} else if constexpr (I % 3 == 0){
return "Fizz";
} else {
return I;
}
}
template <std::size_t...Is>
constexpr auto fizzbuzz_impl(std::index_sequence<Is...>){
return std::make_tuple(fizzbuzz_elem<1 Is>()...);
}
template <std::size_t N>
constexpr auto fizzbuzz(){
return fizzbuzz_impl(std::make_index_sequence<N>());
}
int main() {
constexpr auto res = fizzbuzz<42>();
std::apply([](auto... e){ ((std::cout << e << std::endl), ...); }, res);
}
Ответ №2:
Один из способов сделать что-то подобное — использовать frozen
библиотеку, которая работает в C 14, а ее части работают в C 11. (Мы использовали это в производстве в некотором коде C 11.)
Есть пара вещей, которые библиотека предлагает для того, чтобы constexpr
произошло:
В то время как std::string
можно в конечном итоге вызывать распределитель динамической памяти, что не constexpr
удобно (если только они не добились больших успехов в последних стандартах, которые я пропустил?) frozen::string
по сути, это диапазон строк, указывающий на строковую константу. Итак, если ваша структура данных constexpr
инициализируется, frozen::string
никогда не выполняет выделение, и именно поэтому она может быть удобной для constexpr.
Предполагается, что замороженные контейнеры имеют API, очень похожий на контейнеры C stdlib, но они не могут быть изменены после создания. Кроме того, они очень эффективны во время выполнения — карты основаны на создании идеальной хэш-таблицы во время компиляции, и они также не выделяют динамическую память.
Вот пример:
#include <frozen/unordered_map.h>
#include <frozen/string.h>
constexpr frozen::unordered_map<frozen::string, int, 2> olaf = {
{"19", 19},
{"31", 31},
};
constexpr auto val = olaf.at("19");
Это может быть очень полезно, если у вас есть куча строковых констант, которые необходимо сопоставить со значениями конфигурации для вашего программного обеспечения, или наоборот.
const
переменные в области видимости файла, которые constexpr
инициализируются, не имеют традиционной статической инициализации начиная с C 11. Это означает, что их конструкторы не вызываются до ввода main , нет никакого «фиаско статического порядка инициализации». Вместо этого они попадают в сегмент памяти BSS, доступный только для чтения, в исполняемый файл с уже установленными правильными значениями. Если у вас много таких карт или они большие, это может значительно увеличить время запуска вашего приложения, поскольку вы не вызываете malloc и не копируете много строк перед вводом main.
Комментарии:
1. Не могли бы вы создать карту программно? Вероятно, основная проблема заключается в части «int to string».
2. Я думаю, вы можете создать карту программно, если вы можете создать
std::array
пар, которые входят в карту программно — это не сработает, если вы не можете этого сделать.3. Я помню, что в стеке есть
cvector
тип, который являетсяconstexpr
вектором, с заданной максимальной емкостью, который мы использовали для реализации идеальной части хэширования. Я не могу вспомнить, является ли это частью общедоступного API, хотя4. эта вещь выглядит уместной для сообщения OP: github.com/serge-sans-paille/frozen/blob/master/include/frozen /…
Ответ №3:
std::vector и std::to_string() не являются constexpr. Ваш второй пример функции будет работать без них и при использовании std::string_view
, например:
constexpr std::string_view fizzbuzz(const int k){ if(k % 3 == 0) return "Fizz"; else return "Buzz"; }
Я считаю, что std::string_view — это c 17
Комментарии:
1. Эй, спасибо, но я не хочу возвращать «Buzz» в else, кроме to_string версии k
Ответ №4:
Итак, с помощью @Jarod42 я наконец решил это:
Смотрите комментарии, почему это зачеркнуто.
template <int N>
constexpr std::array<char[9], N> solution8(){
std::array<char[9], N> result{};
for(int i = 0; i < N; i ){
int k = i 1;
if ((k % 3 == 0) amp;amp; (k % 5 == 0)){
sprintf(result[i], "FizzBuzz");
}
else if (k % 3 == 0){
sprintf(result[i], "Fizz");
}
else if (k % 5 == 0){
sprintf(result[i], "Buzz");
}
else {
sprintf(result[i], "%d", i 1);
}
}
return resu<
}
Комментарии:
1. Я не думаю, что
sprintf
это constexpr2. @GuillaumeRacicot тем не менее, он компилируется, разве это не достаточное доказательство?
3. Нет. Компиляторы имеют расширение, специфичное для реализации поведение и другие вещи, которые могут сделать код непереносимым. На самом деле, ни один из трех основных компиляторов не способен скомпилировать ваш код .
4. @GuillaumeRacicot О, спасибо, я не добавил «constexpr» в main. Удаление того, что он компилирует — я думаю, мне следует больше прочитать о constexpr. Спасибо за комментарий.