文字和constexpr函数,编译时评估

时间:2013-06-21 13:03:09

标签: c++ gcc c++11 constexpr user-defined-literals

尝试实现一个令人满意的(简单,直接,没有TMP,没有宏,没有不可读的复杂代码,使用它时没有奇怪的语法)通过用户定义的文字进行编译时哈希,我发现显然GCC对什么是a的理解不断的表达与我的理解截然不同。

因为代码和编译器输出超过一千个单词,所以没有进一步的麻烦:

#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函数使其可以作为枚举的初始化器或模板参数,换句话说,它应该导致在编译时进行评估。 /> 当然,编译器原则上总是 allowable 在运行时评估constexpr函数,但是能够将评估移动到编译时是{{1}的全部要点。毕竟。

我的谬误在哪里,有没有办法实现一个用户定义的文字,它可以采用字符串文字,以便在编译时实际评估?

可能相关的类似问题:
Can a string literal be subscripted in a constant expression?
User defined literal arguments are not constexpr?
第一个似乎表明至少对constexpr这个有效,并且GCC接受它,尽管我承认不能遵循这个结论。
第二个使用整数文字,而不是字符串文字,最后通过使用模板元编程(我不想要)来解决问题。显然问题不仅限于字符串文字?

1 个答案:

答案 0 :(得分:3)

我手边没有GCC 4.7.2可以尝试,但是没有静态断言的代码(后面会有更多内容)编译得很好,并且在编译时使用GCC 4.7.3和{{}执行函数3}}。我想你必须更新你的编译器。

并不总是允许编译器将评估移动到运行时:某些上下文(如模板参数和static_assert)需要在编译时进行评估,如果不可能则需要出错。如果在static_assert中使用UDL,则会强制编译器在编译时对其进行评估。在我的两次测试中都是如此。

现在,转到__builtin_constant_p(str)。首先,如文档所述,__builtin_constant_p可以产生错误否定(即,有时候它可以为常量表达式返回0)。

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

静态断言在编译器看到函数时进行测试,而不是编译器看到的每次调用一次。这使得你总是在编译时上下文中调用它无关紧要:只有正文很重要。因为str有时可能是非常量表达式,所以在该上下文中__builtin_constant_p(str)不能为真:它可能产生错误否定,但不会产生误报。

为了更清楚:static_assert(__builtin_constant_p("blah"), "")将通过(好吧,理论上它可能会失败,但我怀疑编译器会在这里产生假阴性),因为"blah" 总是一个常量表达式,但str"blah"的表达式不同。

为了完整性,如果有问题的参数是数字类型(稍后会详细介绍),并且您在静态断言之外进行了测试,那么如果有GCC 4.8你传递了一个常量,如果你传递了一个非常数则为假。在静态断言中,它是the test to return true

但是! __builtin_constant_p {{1}}显示了一个有趣的细节:

  

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

正如您所看到的,如果给定的表达式是字符串常量,则内置函数具有限制always fails