在编译时计算Cstring的长度。 这真的是一个constexpr?

我想在编译时计算string文字的长度。 为此,我使用以下代码:

#include <cstdio> int constexpr length(const char* str) { return *str ? 1 + length(str + 1) : 0; } int main() { printf("%d %d", length("abcd"), length("abcdefgh")); } 

一切都按预期工作,程序打印4和8.由clang生成的汇编代码显示结果是在编译时计算的:

 0x100000f5e: leaq 0x35(%rip), %rdi ; "%d %d" 0x100000f65: movl $0x4, %esi 0x100000f6a: movl $0x8, %edx 0x100000f6f: xorl %eax, %eax 0x100000f71: callq 0x100000f7a ; symbol stub for: printf 

我的问题:这是标准保证length函数将被评估编译时间?

如果这是真的编译时string文字计算的门刚刚为我打开…例如,我可以在编译时计算散列和更多…

常量expression式不保证在编译时进行评估,我们只从草案C ++标准 5.19节中有一个非规范引用。

[…]>注意:常量expression式可以在翻译过程中进行评估。

您可以将结果赋给constexprvariables以确保它在编译时被评估,我们可以从Bjarne Stroustrup的C ++ 11参考资料中看到这一点( 强调我的 ):

除了能够在编译时评估expression式,我们希望能够在编译时要求expression式进行评估; 在一个variables定义之前的constexpr会这样做 (并暗示const):

例如:

 constexpr int len1 = length("abcd") ; 

Bjarne Stroustrup给出了这个isocpp博客条目中什么时候可以确保编译时间评估的总结,并说:

Herb所说的正确答案是,根据标准,constexpr函数可以在编译器时间或运行时进行评估,除非它用作常量expression式,在这种情况下,它必须在编译时进行评估-时间。 为了保证编译时评估,我们必须在需要常量expression式的地方使用它(例如,作为数组绑定或者作为一个case标签),或者用它来初始化一个constexpr。 我希望没有自尊心的编译器会错过我最初所说的优化机会:“如果所有参数都是常量expression式,constexpr函数将在编译时进行评估”。

因此,这概述了两个应在编译时进行评估的情况:

  1. 在需要一个常量expression式的地方使用它,这似乎是标准草案中的任何地方,短语shall be ... converted constant expressionshall be ... constant expression ,例如数组绑定。
  2. 如上所述,用它来初始化一个constexpr

找出一个constexpr函数的调用是否会产生一个核心常量expression式或仅仅是被优化是很容易的:

在需要常量expression式的上下文中使用它。

 int main() { constexpr int test_const = length("abcd"); std::array<char,length("abcdefgh")> test_const2; } 

注意,现代编译器(如gcc-4.x)在编译时对string进行strlen ,因为它通常被定义为一个内部函数 。 没有启用优化。 虽然结果不是编译时间常量。

例如:

 printf("%zu\n", strlen("abc")); 

结果是:

 movl $3, %esi # strlen("abc") movl $.LC0, %edi # "%zu\n" movl $0, %eax call printf 

让我提出另一个函数,它在编译时计算一个string的长度而不recursion。

 template< size_t N > constexpr size_t length( char const (&)[N] ) { return N-1; } 

看看这个示例代码在ideone 。

不能保证在编译时评估一个constexpr函数,尽pipe任何合理的编译器都会在适当的优化级别启用它。 另一方面,模板参数必须在编译时进行评估。

我使用了下面的技巧在编译时强制进行评估。 不幸的是,它仅适用于整数值(即不适用于浮点值)。

 template<typename T, T V> struct static_eval { static constexpr T value = V; }; 

现在,如果你写

 if (static_eval<int, length("hello, world")>::value > 7) { ... } 

你可以确定if语句是一个编译时常量,没有运行时间的开销。

维基百科对广义常量expression式的简短解释:

在函数上使用constexpr会对函数的function施加一些限制。 首先,函数必须有一个非空的返回types。 其次,函数体不能声明variables或定义新的types。 第三,主体可能只包含声明,空语句和单个返回语句。 必须存在参数值,以便在参数replace之后,return语句中的expression式会生成一个常量expression式。

在函数定义之前使用constexpr关键字指示编译器检查是否满足这些限制。 如果是,并且函数被调用一个常量,则返回的值保证是常量,因此可以在任何需要常量expression式的地方使用。