Как создать constexpr std::vector или что-то подобное?

#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.

https://github.com/serge-sans-paille/frozen

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

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 это constexpr

2. @GuillaumeRacicot тем не менее, он компилируется, разве это не достаточное доказательство?

3. Нет. Компиляторы имеют расширение, специфичное для реализации поведение и другие вещи, которые могут сделать код непереносимым. На самом деле, ни один из трех основных компиляторов не способен скомпилировать ваш код .

4. @GuillaumeRacicot О, спасибо, я не добавил «constexpr» в main. Удаление того, что он компилирует — я думаю, мне следует больше прочитать о constexpr. Спасибо за комментарий.