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

问题描述

所以我一直在四处寻找不同的东西,但是我无法用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 ++ Standard越低越好。

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

解决方法

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本质上是指向字符串常量的字符串跨度。因此,如果constexpr的数据结构正在初始化,则frozen::string永远不会进行分配,这就是为什么它可以对constexpr友好的原因。

冻结的容器具有与C ++ stdlib容器非常相似的API,但在构造后不可修改。此外,它们在运行时非常高效-映射基于在编译时创建完美的哈希表的基础,并且它们也不进行任何动态内存分配。

这是一个例子:

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

如果您有一堆字符串常量需要映射到软件的配置值,反之亦然,这将非常有用。

自C ++ 11起,在文件范围内已初始化const

constexpr变量没有传统的静态初始化。这意味着,在输入main之前不调用其构造函数,不存在“静态初始化顺序失败”。相反,它们最终在可执行文件的BSS只读存储器段中具有正确的值。如果您有很多这样的映射,或者它们很大,那么由于不调用malloc并在输入main之前复制了很多字符串,因此可以显着地提高应用程序的启动时间。

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

,

std :: vector和std :: to_string()不是constexpr。 您的第二个函数示例将在不使用这些函数的情况下并且在使用case class User(id: String,name: String) val oldUsers = Seq(User("1","Test1"),User("2","Test2"),User("8","Test8"),User("9","Test9")) val newUsers = Seq(User("1","UpdatedTest1"),User("10","UpdatedTest10")) val oldAsMap = oldUsers.map(user => user.id -> user).toMap val newAsMap = newUsers.map(user => user.id -> user).toMap val ids = oldAsMap.keySet ++ newAsMap.keySet val result = ids.map(id => newAsMap.getOrElse(id,oldAsMap(id))).toSeq 时工作,例如:

std::string_view

我相信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\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;
}