首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >对std::map初始化使用c++ 11 constexpr

对std::map初始化使用c++ 11 constexpr
EN

Stack Overflow用户
提问于 2017-11-12 11:12:31
回答 5查看 30.5K关注 0票数 16

我想用键为constexpr初始化一个std::map。考虑以下C++11 MWE:

代码语言:javascript
运行
复制
#include <map>
using std::map;

constexpr unsigned int str2int(const char* str, const int h = 0) {
    return !str[h] ? 5381 : (str2int(str, h + 1) * 33) ^ str[h];
}

const map<unsigned int, const char*> values = {
    {str2int("foo"), "bar"},
    {str2int("hello"), "world"}
};

int main() { return 0; }

当代码编译最近的clang和gcc时,生成的二进制将包含键类型的字符串:

为什么这些键包含在二进制文件中,即使它们被用作警察的呢?有办法解决这个问题吗?

当然,映射初始化将在运行时进行。但是,二进制文件中的值不应该在编译时被参数替换吗?

注:这当然是一个简化的例子。我知道有不同的boost结构可能更适合这个用例。我特别感兴趣的是,为什么会发生这种情况。

编辑

无论是否启用优化,都会发生这种行为。下面的代码编译时,bar是字符串表中唯一用户定义的字符串:

代码语言:javascript
运行
复制
#include <map>
#include <iostream>
#include <string>

using namespace std;

constexpr unsigned int str2int(const char* str, const int h = 0) {
  return !str[h] ? 5381 : (str2int(str, h + 1) * 33) ^ str[h];
}

int main() {
  string input;
  while(true) {
    cin >> input;
    switch(str2int(input.c_str())) {
      case str2int("quit"):
      return 0;
      case str2int("foo"):
      cout << "bar" << endl;
    }
  }
}

为了验证结果,我使用了一个小型shell脚本

代码语言:javascript
运行
复制
$ for x in "gcc-mp-7" "clang"; do 
  $x --version|head -n 1
  $x -lstdc++ -std=c++11 -Ofast constexpr.cpp -o a
  $x -lstdc++ -std=c++1z -Ofast constexpr.cpp -o b
  strings a|grep hello|wc -l
  strings b|grep hello|wc -l
done

gcc-mp-7 (MacPorts gcc7 7.2.0_0) 7.2.0
       1
       0
Apple LLVM version 8.1.0 (clang-802.0.38)
       1
       0
EN

回答 5

Stack Overflow用户

发布于 2018-12-20 00:27:41

仅仅声明为const是不够的。这些字符串包含在二进制文件中,因为:

代码语言:javascript
运行
复制
const map<unsigned int, const char*> values

是康斯特,但不是警察。当程序启动时,它将运行“str2int”,而不是在编译时运行。const只会保证它不会允许进一步的修改,但不会使编译时间受到影响。

它的接缝,你正在寻找的Serge的冷冻集装箱- https://github.com/serge-sans-paille/frozen

虽然我不知道它是否能在C++11上工作,但是如果你想提高性能,那肯定值得一试。

您可以创建在编译时进行散列的映射,并将为您提供产生完美哈希函数的额外好处--允许在O(1)时间(恒定时间)访问所有键。

它确实是gperf非常有能力的替代品。

目前,Clang和GCC对您在编译时能够处理的键的数量施加了限制。在我的1G RAM VPS上生成一个带有2048个键的地图是可以的,只有clang才行。GCC现在更糟了,他会更快地吃掉你所有的内存。

票数 2
EN

Stack Overflow用户

发布于 2017-11-12 11:22:34

我不能用g++ (主干)或clang++ (主干)进行复制。我使用了以下标志:-std=c++1z -Ofast。然后,我用strings检查了编译后的二进制文件的内容:"foo""hello"都没有。

是否已启用优化进行编译?

无论如何,使用str2int并不强制进行编译时评估。为了强迫它,你可以:

代码语言:javascript
运行
复制
constexpr auto k0 = str2int("foo");
constexpr auto k1 = str2int("hello");

const map<unsigned int, const char*> values = {
    {k0, "bar"},
    {k1, "world"}
};
票数 1
EN

Stack Overflow用户

发布于 2017-11-12 11:39:32

不能使用GCC 7.2、clang5.0或MSVC 17中的--std=c++11 -O2复制您的问题。

演示

是否在(-g)上使用调试符号进行构建?这可能就是你看到的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47248107

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档