如何创建 constexpr std::vector<std::string> 或类似的东西?

How to create a constexpr std::vector<std::string> or something similar?

所以我一直在四处寻找并尝试不同的东西,但我无法理解我将如何使用 constexpr 创建一些字符串集合。

我想做的基本上是以下内容,显然无法编译:

constexpr std::vector<std::string> fizzbuzz(){
    size_t N = 100;

    std::vector<std::string> result;
    result.reserve(N);

    for (int i = 0; i < N; i++){
        int k = i+1;

        if(k % 5 == 0 && 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 result;
}

如果我知道如何做像这样简单的事情,我会很高兴:

constexpr std::string fizzbuzz(int k){
    if(k % 3 == 0) return "Fizz";
    else return std::to_string(k);
}

我认为这只是向完整解决方案迈出的一小步。 不一定是 std::strings 也不一定是 std::vectors.

哦,C++ 标准越低越好。

已编辑以帮助更好地理解问题。

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

所以在@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) && (k % 5 == 0)){
            sprintf(result[i], "FizzBuzz[=10=]");
        }
        else if (k % 3 == 0){
            sprintf(result[i], "Fizz[=10=]");
        }
        else if (k % 5 == 0){
            sprintf(result[i], "Buzz[=10=]");
        }
        else {
            sprintf(result[i], "%d[=10=]", i+1);
        }
    }

    return result;
}

std::vector/std::string 在 C++20 之前没有 constexpr 构造函数... 即使在 C++20 中,constexpr 分配也不应从 constexpr 求值中逃脱,因此不能在运行时使用(用于打印)。

我没有看到将整数转换为 char 序列表示的标准 constexpr 方法。 std::to_stringstd::to_charsstd::format 不是 constexpr

因此,您可以使用 std::tuple 而不是同构容器,例如 (C++17):

template <std::size_t I>
constexpr auto fizzbuzz_elem()
{
    if constexpr (I % 5 == 0 && 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);
}

Demo

做这种事情的一种方法是使用 frozen 库,它在 C++14 中工作,它的一部分在 C++11 中工作。 (我们在一些 C++11 代码的生产中使用了它。)

图书馆提供了一些东西来实现 constexpr

虽然 std::string 最终可以调用动态内存分配器,但这并不 constexpr 友好(除非他们在我错过的最新标准中取得了重大进展?)frozen::string 是基本上是一个 string-span 指向一个字符串常量。因此,如果您的数据结构正在 constexpr 初始化,frozen::string 永远不会进行分配,这就是为什么它可以是 constexpr 友好的。

冻结容器与 C++ stdlib 容器非常相似 API,但它们在构造后不可修改。此外,它们在 run-time 方面非常高效——这些映射基于在编译时创建一个完美的散列 table,并且它们也不进行任何动态内存分配。

这是一个例子:

#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");

如果您有一堆字符串常量需要映射到软件的配置值,或者 vice-versa。

,这将非常有用

const file-scope 处的 constexpr 初始化的变量自 C++11 以来没有传统的静态初始化。这意味着,在进入 main 之前不会调用它们的构造函数,不存在“静态初始化顺序失败”。相反,它们最终出现在 executable 的 BSS read-only 内存段中,并且正确的值已经到位。如果您有很多这样的映射或者它们很大,这可以显着缩短应用程序的启动时间,因为在输入 main 之前不调用 malloc 并复制大量字符串。

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