【问题标题】:Compile-time population of data structures other than arrays?编译时填充数组以外的数据结构?
【发布时间】:2013-03-07 14:19:08
【问题描述】:

在 C++ 中,您可以这样做:

static const char * [4] = {
   "One fish",
   "Two fish",
   "Red fish",
   "Blue fish"
};

... 这为您提供了一个很好的只读数组数据结构,它在运行时不需要任何 CPU 周期来初始化,因为所有数据都已为您布置好(在可执行文件的只读内存中)页)由编译器。

但是如果我宁愿使用不同的数据结构而不是数组呢?例如,如果我希望我的数据结构能够通过键快速查找,我必须这样做:

static std::map<int, const char *> map;

int main(int, char **)
{
   map.insert(555, "One fish");
   map.insert(666, "Two fish");
   map.insert(451, "Red fish");
   map.insert(626, "Blue fish");

   [... rest of program here...]
}

...由于地图数据结构在运行时被填充,因此不太优雅且效率较低,即使所有必要的数据在编译时都是已知的,因此(理论上)可以完成这项工作。

我的问题是,在 C++(或 C++11)中有没有办法创建一个只读数据结构(例如地图),其数据完全在编译时设置,因此预先填充并准备就绪在运行时使用,数组的方式可以是什么?

【问题讨论】:

    标签: c++ c++11 compile-time


    【解决方案1】:

    如果您想要地图(或集合),请考虑使用binary tree stored as an array。您可以在调试版本中断言它在运行时已正确排序,但在优化版本中,您可以假设一切都已正确安排,然后可以执行与 std::map 中相同的二进制搜索操作,但使用底层storage 是一个数组。只需编写一个小程序,在将数据粘贴到您的程序之前为您堆放数据。

    【讨论】:

    • 堆的顺序不足以进行二分搜索。你需要一个实际的二叉搜索树(或道德等价物)来做到这一点。
    • 哦,你当然是对的。我会更新我的答案(这将与以前保持相似,只是不是堆)。
    【解决方案2】:

    不容易,不。如果您尝试使用malloc 来执行您的第一个示例,显然它在编译时不起作用。由于每个标准容器都使用new(好吧,std::allocator&lt;T&gt;::allocate(),但我们现在假设它是new),我们不能在编译时这样做。

    话虽如此,这取决于你愿意经历多少痛苦,以及你想推迟多少编译时间。您当然不能仅使用标准库功能来做到这一点。另一方面,使用boost::mpl...

    #include <iostream>
    
    #include "boost/mpl/map.hpp"
    #include "boost/mpl/for_each.hpp"
    #include "boost/mpl/string.hpp"
    #include "boost/mpl/front.hpp"
    #include "boost/mpl/has_key.hpp"
    
    using namespace boost::mpl;
    
    int main()
    {
        typedef string<'One ', 'fish'> strone;
        typedef string<'Two ', 'fish'> strtwo;
        typedef string<'Red ', 'fish'> strthree;
        typedef string<'Blue', 'fish'> strfour;
    
        typedef map<pair<int_<555>, strone>,
            pair<int_<666>, strtwo>,
            pair<int_<451>, strthree>,
            pair<int_<626>, strfour>> m;
    
        std::cout << c_str<second<front<m>::type>::type>::value << "\n";
        std::cout << has_key<m, int_<666>>::type::value << "\n";
        std::cout << has_key<m, int_<111>>::type::value << "\n";
    }
    

    【讨论】:

    【解决方案3】:

    值得一提的是,您的问题源于您使用地图这一事实。 地图经常被过度使用。 地图的替代解决方案是排序的向量/数组。只有在用于存储长度未知的数据或(并且仅在某些情况下)数据频繁更改时,地图才会比地图“更好”。

    函数 std::sort, std::lower_bound/std::upper_bound 是你需要的。 如果你可以自己对数据进行排序,你只需要一个函数,lower_bound,数据可以是const。

    【讨论】:

    • “地图只会变得比地图“更好”吗?你的意思是“比向量更好”吗?
    • map 只是一个例子......你可以替换任何(非数组)数据结构,问题是一样的。
    • @JeremyFriesner:是的,我知道这是一个例子。我的意思是“地图变得比地图更好”听起来像是一个错字。
    • @Adri 我正在回复 user2186945 的声明,即问题在于我使用的是地图,而不是您的评论。
    • @JeremyFriesner,啊,好吧!对不起我的错误!
    【解决方案4】:

    是的,C++11 允许大括号初始值设定项:

    std::map<int, const char *> map = {
      { 555, "One fish" },
      { 666, "Two fish" },
      // etc
    };
    

    【讨论】:

    • 是的,但这将在运行时构建数据结构,这是 OP 想要避免的。
    • @JohnZwinck 啊,我的错,我理解 OP 只是想要一种与静态初始化兼容的语法。重新阅读他的问题,他的要求很明显。真丢人。
    • 顺便说一句,你能把初始化列表构造成一个 const std::map 吗?
    • @Inverse:是的,它也适用于const。 (至少我的错误似乎对 OP 以外的其他人有用;)
    猜你喜欢
    • 2013-01-20
    • 2014-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-14
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多