C++ 文本和constexpr函数,编译时计算

C++ 文本和constexpr函数,编译时计算,c++,gcc,c++11,constexpr,user-defined-literals,C++,Gcc,C++11,Constexpr,User Defined Literals,试图通过用户定义的文本实现一个令人愉悦的(简单、直接、没有TMP、没有宏、没有不可读的卷积代码、使用时没有奇怪的语法)编译时哈希,我发现GCC对常量表达式的理解显然与我的理解大不相同 由于代码和编译器输出的字数超过1000个字,因此无需进一步讨论: #include <cstdio> constexpr unsigned int operator"" _djb(const char* const str, unsigned int len) { static_assert(

试图通过用户定义的文本实现一个令人愉悦的(简单、直接、没有TMP、没有宏、没有不可读的卷积代码、使用时没有奇怪的语法)编译时哈希,我发现GCC对常量表达式的理解显然与我的理解大不相同

由于代码和编译器输出的字数超过1000个字,因此无需进一步讨论:

#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;
}
我的理解是字符串文字是,嗯。。。文字。换句话说,编译时间常数。具体地说,它是一个编译器时已知的常量字符序列,从编译器指定的常量地址开始(因此是已知的),由
'\0'
终止。这在逻辑上意味着,提供给
运算符“
的文本编译器计算长度也是
constepr

另外,我的理解是,只使用编译时参数调用
constexpr
函数,使其可以作为枚举的初始值设定项或模板参数,换句话说,它应该在编译时进行计算。
当然,原则上总是允许编译器在运行时对
constexpr
函数求值,但能够将求值移动到编译时毕竟是拥有
constexpr
的关键

我的谬误在哪里?有没有一种方法可以实现一个用户定义的文本,它可以接受一个字符串文本,从而在编译时进行计算

可能相关的类似问题:


第一个似乎表明,至少对于
char-const(&str)[N]
来说,这是可行的,GCC接受这一点,尽管我承认不能得出结论。

第二个使用整数文本,而不是字符串文本,最后通过使用模板元编程(我不希望这样)来解决这个问题。显然,问题并不局限于字符串文字?

我手头没有GCC4.7.2可供尝试,但您的代码没有静态断言(稍后将详细介绍)可以很好地编译,并在编译时使用and和执行函数。我想你必须更新你的编译器

编译器并不总是允许将计算移动到运行时:某些上下文,如模板参数和
静态断言
,需要在编译时进行计算,如果不可能,则会出现错误。如果在
static\u assert
中使用UDL,则可能的话,将强制编译器在编译时对其求值。在我的两次测试中,它都是这样做的

现在,转到
\u内置常数\u p(str)
。首先,如文件所述,
\uuuuu内置常量\up
可能会产生误判(即,对于常量表达式,它有时会返回0)

str
不是可证明的常量表达式,因为它是函数参数。在某些上下文中,您可以强制编译器在编译时对函数求值,但这并不意味着它永远无法在运行时对函数求值:某些上下文从不强制编译时求值(事实上,在某些上下文中,编译时求值是不可能的)
str
可以是非常量表达式

静态断言是在编译器看到函数时测试的,而不是在编译器看到的每个调用中测试一次。这使得您总是在编译时上下文中调用它这一事实变得无关紧要:只有身体才重要。由于
str
有时可能是一个非常量表达式,因此在该上下文中,
内置常量p(str)
不能为真:它可以产生假阴性,但不会产生假阳性

更清楚地说:
static\u assert(uu builtin\u constant\u p(“blah”),“)
将通过(理论上可能会失败,但我怀疑编译器在这里会产生假阴性),因为
“blah”
始终是一个常量表达式,但
str
“blah”
的表达式不同

为了完整性,如果所讨论的参数是数值类型(稍后将详细介绍),并且您在静态断言之外进行了测试,那么如果您传递了一个常量,则可以得到false,如果您传递了一个非常量,则可以得到false。在静态断言中,它是

但是!对于
\u内置常数\u p
显示一个有趣的细节:

但是,如果在内联函数中使用它,并将函数的参数作为参数传递给内置函数,则当使用字符串常量或复合文字(请参阅复合文字)调用内联函数时,GCC将永远不会返回1除非指定-O选项,否则在将常量数值传递给内联函数时,不会返回1


如您所见,如果给定的表达式是字符串常量,则内置函数有一个限制。

@R.MartinhoFernandes:感谢您提供
std::size\t
指针。因此,如果它在4.7.3和4.8中运行良好,那么它似乎是gcc 4.7.2的限制。事实上,如果我尝试将其用于
枚举
,我会得到“STRING_USERDEF'标记之前的预期标识符”,因此显然我的GCC版本不会选择在编译时不求值,但它根本不能。如果您愿意,请添加您的发现作为答案,我将接受它作为“必须升级编译器”(meh,compile gcc…)。好的,我添加了一个带有我的发现的答案和一些关于
\uuuuu内置常量\up
的信息。我现在正在删除注释,因为它们现在是多余的。@Damon您可以找到一个有趣的用例以及该功能的一些背景。哇,您在
\uuuuuuu内置常量\up
文档中挖掘的注释特别有趣。我根本不知道这个条款。同时,我升级到了gcc-4.8.1。有趣的是,除非我强制它(通过分配给
enum
),否则它在编译时似乎仍然不会求值。有趣的是,打印
enum
值,然后用s调用
constepr
函数
[...]\main.cpp: In function 'constexpr unsigned int operator"" _djb(const char*, unsigned int)':
[...]\main.cpp:5:2: error: static assertion failed: huh?