使用c ++ 11 constexpr进行std :: map初始化

muf*_*fel 7 c++ constexpr c++11

我想初始化一个std :: map,键是a constexpr.考虑以下C++ 11 MWE:

#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; }
Run Code Online (Sandbox Code Playgroud)

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

C字符串文字

为什么密钥包含在二进制文件中,即使它们被用作constexpr?有办法解决这个问题吗?

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

注意:这当然是一个简化的例子.我知道有不同的增强结构可能更适合这种用例.我特别感兴趣的是为什么会这样.

[编辑]

无论是否启用优化,都会发生此行为.以下代码使用bar作为字符串表中唯一的用户定义字符串进行编译:

#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;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

$ 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
Run Code Online (Sandbox Code Playgroud)

sta*_*y67 4

这个线程并不是很新鲜,但有时仍然需要坚持使用 c++11 :|

如何使用 constexpr 函数来设置键:

constexpr int makeKey(const char* s) { // c++ refused 'auto' here
  return str2int(s); // using str2int from above
}

const std::map<unsigned int, const char*> values = {
    {k0, "bar"}, // these require another declaration (see above) 
    {k1, "world"}, 
    {makeKey("its"), "me"} // this initialization is 'single source'
};
Run Code Online (Sandbox Code Playgroud)

一旦此类地图变得更大,“单一来源”密钥就会简化它们的维护......

我的小测试程序

...

int main(int argc, char** argv) {

  for(int i(1);i<argc;++i)  {
    const std::map<unsigned int, const char*>::const_iterator cit(values.find(str2int(argv[i])));
    std::cout << argv[i] << " gets " << (cit==values.cend()?"nothing":cit->second) << std::endl;
  }

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果使用 gcc 7.5 编译,则工作正常并且不包含任何关键字符串

--std=c++11 -O0