在编译时计算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式可以在翻译过程中进行评估。
您可以将结果赋给constexpr
variables以确保它在编译时被评估,我们可以从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式的地方使用。