【发布时间】:2013-06-21 13:03:08
【问题描述】:
尝试通过用户定义的文字实现一个令人愉悦的(简单、直接、没有 TMP、没有宏、没有不可读的复杂代码、没有奇怪的语法)编译时哈希,我发现显然 GCC 对什么是常量表达式与我的理解大相径庭。
既然代码和编译器输出说了一千多个字,那就不用多说了:
#include <cstdio>
constexpr unsigned int operator"" _djb(const char* const str, unsigned int len)
{
static_assert(__builtin_constant_p(str), "huh?");
return len ? str[0] + (33 * ::operator"" _djb(str+1, len-1)) : 5381;
}
int main()
{
printf("%u\n", "blah"_djb);
return 0;
}
代码非常简单,不需要解释太多,也不需要问太多——除了它不在编译时进行评估。我尝试使用指针取消引用而不是使用数组索引以及在!*str 处进行递归中断,结果都相同。
static_assert 是后来在陷入困境时添加的,原因是当我坚信它应该在编译时不会评估哈希值。好吧,令人惊讶的是,这只让我更加困惑,但并没有澄清任何事情!没有static_assert 的原始代码已被广泛接受并且编译时没有警告(gcc 4.7.2)。
编译器输出:
[...]\main.cpp: In function 'constexpr unsigned int operator"" _djb(const char*, unsigned int)':
[...]\main.cpp:5:2: error: static assertion failed: huh?
我的理解是字符串文字是,嗯... 文字。换句话说,一个编译时常量。具体来说,它是一个编译时已知的常量字符序列,从编译器分配的常量地址开始(因此是已知的),以'\0' 终止。这在逻辑上意味着提供给operator"" 的文字的编译器计算长度也是constexpr。
另外,我的理解是,仅使用编译时参数调用 constexpr 函数使其可以作为枚举或模板参数的初始化程序,换句话说,它应该导致在编译时进行评估。
当然,原则上总是允许编译器在运行时评估constexpr 函数,但是能够将评估移至编译时是拥有constexpr 的全部意义,之后全部。
我的谬误在哪里,有没有一种方法可以实现用户定义的文字,它可以采用字符串文字,以便在编译时实际评估?
可能相关的类似问题:
Can a string literal be subscripted in a constant expression?
User defined literal arguments are not constexpr?
第一个似乎表明至少对于 char const (&str)[N] 这是有效的,并且 GCC 接受它,尽管我承认无法得出结论。
第二个使用整数文字,而不是字符串文字,最后通过使用模板元编程(我不想要)解决了这个问题。那么显然问题不仅限于字符串文字?
【问题讨论】:
-
@R.MartinhoFernandes:感谢
std::size_t指针。因此,如果它适用于 4.7.3 和 4.8,它似乎是 gcc 4.7.2 的限制。事实上,如果我尝试将其用于enum,我会得到“'STRING_USERDEF' 令牌之前的预期标识符”,所以显然我的 GCC 版本没有选择不要在编译时评估,但它根本不能。如果您愿意,请将您的发现添加为答案,我会接受它作为“必须升级编译器”(meh,compile gcc ...)。 -
好的,我添加了我的发现的答案以及一些关于
__builtin_constant_p的信息。我现在正在删除 cmets,因为它们现在是多余的。 -
@Damon 您可以找到一个有趣的__builtin_constant_p here 用例以及该功能的一些背景。
标签: c++ gcc c++11 constexpr user-defined-literals