我想初始化一个std::map,键为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; }
当代码编译which recent clang和gcc时,生成的二进制文件将包含key类型的字符串:
为什么键包含在二进制文件中,即使它们被用作constexpr?有什么方法可以解决这种行为吗?
当然,Map初始化将在运行时发生。但是二进制文件中的值不应该在编译时被constexpr替换吗?
注:这是一个简单的例子。我知道有不同的 boost 结构可能更适合这个用例。我特别感兴趣的是为什么会这样。
[编辑]
无论是否启用优化,都会发生此行为。下面的代码编译时,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;
}
}
}
为了验证结果,我使用了一个小的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
6条答案
按热度按时间xdnvmnnf1#
这个线程并不新鲜,但有时仍然需要坚持使用c++11:|
如何使用constexpr函数来设置键:
“单一来源”密钥简化了这些Map的维护,一旦它们变得更大……
我的小测试程序
工作正常,如果使用gcc7.5编译,则不包含任何键字符串
wydwbb8l2#
仅仅声明为const是不够的。字符串包含在二进制文件中的原因是:
是const,但不是constexpr。它将在程序启动时运行'str 2 int',而不是在编译时。作为const只能保证它不允许进一步的修改,但不会在编译时妥协。
看起来你正在寻找Serge Sans Paille的Frozen constexpr容器-https://github.com/serge-sans-paille/frozen
虽然我不知道它是否能在C++11上工作,但如果你想提高性能,它绝对值得一试。
您可以创建在编译时进行散列的Map,这将给予您带来产生完美散列函数的额外好处-允许在O(1)时间(常数时间)内访问所有键。
它确实是gperf的一个非常称职的替代品。
目前,Clang和GCC对编译时能够处理的键数有限制。用2048个键制作Map,结果在我的1G RAM VPS上没问题,只有当啷声。GCC目前甚至更糟,并且会更快地吃掉所有的RAM。
z4bn682m3#
旧线程,但也可以使用C++ 17兼容 constexpr hash-map:constexpr-hash-map。
这本质上是我自己制作的一个只有头的散列图结构,以便在编译时在 constexpr 上下文中进行构造和检索(包括查找)。
该库已经为
const char*
进行了专门化,以便能够在编译时比较键,因此它可以解决主要问题。yr9zkbsy4#
我不能用g++(trunk)或clang++(trunk)进行复制。我使用了以下标志:
-std=c++1z -Ofast
。然后我用strings
检查了编译后的二进制文件的内容:"foo"
和"hello"
都不存在。您在编译时启用了优化吗?
无论如何,使用
str2int
不会强制进行编译时求值。为了强制执行,您可以执行以下操作:jv2fixgn5#
无法在GCC 7.2、clang 5.0或MSVC 17中使用
--std=c++11 -O2
重现您的问题。DEMO
您是否在(
-g
)上使用调试符号进行构建?这可能就是你所看到的。7xzttuei6#
这应该强制编译时求值。
在c++14中,它稍微不那么冗长:
c++17:
它适用于大多数没有类型特定版本的基本类型常量。