在编译时计算C字符串的长度。这真的是一个constexpr吗?

时间:2014-09-17 12:38:31

标签: c++ c++11 standards constexpr string-literals

我试图在编译时计算字符串文字的长度。为此,我使用以下代码:

#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函数将在编译时进行评估?

如果这是真的,编译时字符串文字计算的大门刚刚为我打开...例如,我可以在编译时计算哈希值等等......

6 个答案:

答案 0 :(得分:66)

不保证在编译时评估常量表达式,我们只有来自draft C++ standard部分5.19 常量表达式的非规范性引用,但这说明: / p>

  

[...]&gt; [注意:可以在期间评估常量表达式   翻译 - 结束说明]

您可以将结果分配给constexpr变量,以确保在编译时对其进行评估,我们可以从Bjarne Stroustrup's C++11 reference看到这一点(强调我的):< / p>

  

除了能够在编译时评估表达式,我们   希望能够要求在编译时评估表达式   时间;变量定义前面的constexpr就是那个(和   暗示const):

例如:

constexpr int len1 = length("abcd") ;

Bjarne Stroustrup总结了我们何时可以确保编译时间评估isocpp blog entry并说:

  

[...]正确答案 - 如上所述   通过Herb - 是根据标准constexpr功能可能   在编译器时间或运行时进行评估,除非它被用作a   常量表达式,在这种情况下必须对其进行求值   编译时间。为了保证编译时评估,我们必须使用   它需要一个常量表达式(例如,作为一个数组绑定或   作为案例标签)或使用它来初始化constexpr。我希望   没有自尊的编译器会错过优化   有机会做我最初说的话:“constexpr功能是   如果所有参数都是常量,则在编译时进行评估   表述“。

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

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

答案 1 :(得分:24)

很容易发现对constexpr函数的调用是否会导致核心常量表达式或仅仅是在优化:

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

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

答案 2 :(得分:16)

请注意,现代编译器(如gcc-4.x)在编译时对字符串文字执行strlen,因为它通常定义为intrinsic function。未启用优化。虽然结果不是编译时常量。

E.g:

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

结果:

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

答案 3 :(得分:13)

让我提出另一个在编译时计算字符串长度而不是递归的函数。

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

看看这个sample code at ideone

答案 4 :(得分:6)

无法保证在编译时评估constexpr函数,但任何合理的编译器都会在启用的适当优化级别执行此操作。另一方面,模板参数必须在编译时进行评估。

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

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

现在,如果你写

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

您可以确定if语句是编译时常量,没有运行时开销。

答案 5 :(得分:1)

维基百科在Generalized constant expressions上的条目的简短解释:

  

在函数上使用constexpr会对函数的作用施加一些限制。首先,该函数必须具有非void返回类型。其次,函数体不能声明变量或定义新类型。第三,正文可能只包含声明,空语句和单个return语句。必须存在参数值,以便在参数替换后,return语句中的表达式生成常量表达式。

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