在编译时计算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函数将在编译时进行评估”。
因此,这概述了两个应在编译时进行评估的情况:
-  在需要一个常量expression式的地方使用它,这似乎是标准草案中的任何地方,短语shall be ... converted constant expression或shall be ... constant expression,例如数组绑定。
-  如上所述,用它来初始化一个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式的地方使用。