简体   繁体   English

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

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

So I've been looking around and trying different things but I can't wrap my head around how I would go about creating some collection of strings with constexpr.因此,我一直在环顾四周并尝试不同的方法,但我无法全神贯注地想 go 如何使用 constexpr 创建一些字符串集合。

What I'm trying to do is basically the following, which obviously doesn't compile:我想做的基本上是以下内容,显然无法编译:

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;
}

I would already be happy if I understood how to do something as simple as:如果我知道如何做像下面这样简单的事情,我就已经很高兴了:

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

From there I reckon it's only a small step to the complete solution.从那里我认为这只是完整解决方案的一小步。 It doesn't have to be std::strings it doesn't have to be std::vectors.它不一定是 std::strings,也不一定是 std::vectors。

Oh, and the lower the C++Standard the better.哦,C++ 标准越低越好。

Edited to help understanding the problem better.编辑以帮助更好地理解问题。

std::vector / std::string doesn't have constexpr constructor before C++20... and even in C++20, the constexpr allocation should not escape from constexpr evaluation, so cannot be used in runtime (for printing). std::vector / std::string在 C++20 之前没有constexpr构造函数...甚至在 C++20 中, constexpr分配不应从 constexpr 评估中逃脱,因此不能在运行时使用(用于打印).

I don't see a standard constexpr way to transform an integer to a char sequence representation.我没有看到将 integer 转换为 char 序列表示的标准 constexpr 方法。 std::to_string , std::to_chars , std::format are not constexpr . std::to_stringstd::to_charsstd::format不是constexpr

So instead of homogeneous container, you might use std::tuple , something like (C++17):因此,您可以使用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演示

One way to do this kind of stuff is to use the frozen library, which works in C++14, and parts of it work in C++11. (We used it in production in some C++11 code.)做这种事情的一种方法是使用frozen库,它在 C++14 中工作,它的一部分在 C++11 中工作。(我们在一些 C++11 代码中使用它。)

There are a couple things the library offers to make the constexpr happen:图书馆提供了一些东西来使constexpr发生:

While std::string can make calls ultimately to the dynamic memory allocator, which is not constexpr friendly (unless they made big advances in recent standards that I missed?) frozen::string is basically a string-span pointing to a string constant.虽然std::string最终可以调用动态 memory 分配器,但它不是constexpr友好的(除非它们在我错过的最新标准中取得了重大进展?) frozen::string基本上是一个指向字符串常量的字符串跨度。 So if your data structure is being constexpr initialized, frozen::string never makes an allocation, and that is why it can be constexpr friendly.因此,如果您的数据结构正在被constexpr初始化, frozen::string永远不会进行分配,这就是它可以对 constexpr 友好的原因。

The frozen containers are meant to have API very similar to the C++ stdlib containers, but they are not modifiable after construction.冻结的容器意味着 API 与 C++ stdlib 容器非常相似,但它们在构造后不可修改。 Additionally, they are very efficient at run-time -- the maps are based on creating a perfect hash table, at compile time, and they don't make any dynamic memory allocations either.此外,它们在运行时非常高效——映射基于在编译时创建完美的 hash 表,并且它们也不进行任何动态 memory 分配。

Here's an example:这是一个例子:

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

This can be very useful if you have a bunch of string constants that need to map to configuration values for your software, or vice-versa.如果您有一堆字符串常量需要 map 到您的软件的配置值,这将非常有用,反之亦然。

const variables at file-scope which are constexpr initialized do not have traditional static initialization since C++11. This means, their constructors are not called before main is entered, there is no "static initialization order fiasco".自 C++11 以来, constexpr初始化的文件范围内的const变量没有传统的 static 初始化。这意味着,在进入 main 之前不会调用它们的构造函数,没有“静态初始化顺序失败”。 Instead they end up in the BSS read-only memory segment of the executable with the right values already in place.相反,它们最终出现在可执行文件的 BSS 只读 memory 段中,其中正确的值已经到位。 If you have many such maps or they are large, this can measurably improve the startup time of your application, due to not calling malloc and copying a lot of strings around before main is entered.如果你有很多这样的映射或者它们很大,这可以显着缩短你的应用程序的启动时间,因为在输入 main 之前没有调用 malloc 并且复制了很多字符串。

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

std::vector and std::to_string() are not constexpr. std::vector 和 std::to_string() 不是 constexpr。 Your second function example will work without those and while using std::string_view , like:您的第二个 function 示例将在没有这些的情况下使用std::string_view ,例如:

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

I believe std::string_view is c++17我相信 std::string_view 是 c++17

So with the help of @Jarod42 I solved it at last:所以在@Jarod42 的帮助下,我终于解决了它:

See comments for why this is striked through.请参阅注释以了解为什么要划线。

 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\0"); } else if (k % 3 == 0){ sprintf(result[i], "Fizz\0"); } else if (k % 5 == 0){ sprintf(result[i], "Buzz\0"); } else { sprintf(result[i], "%d\0", i+1); } } return result; }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM