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

Mir*_*pas 86 c++ standards string-literals constexpr c++11

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

#include <cstdio>

int constexpr length(const char* str)
{
    return *str ? 1 + length(str + 1) : 0;
}

int main()
{
    printf("%d %d", length("abcd"), length("abcdefgh"));
}
Run Code Online (Sandbox Code Playgroud)

一切都按预期工作,程序打印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
Run Code Online (Sandbox Code Playgroud)

我的问题:标准是否保证length函数将在编译时进行评估?

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

Sha*_*our 69

不保证在编译时计算常量表达式,我们只有草案C++标准部分5.19 常量表达式的非规范性引用,但是这样说:

[...]> [注意:在翻译过程中可以评估常量表达式.-end note]

您可以将结果分配给constexpr变量以确保在编译时对其进行求值,我们可以从Bjarne Stroustrup的C++ 11参考中看到这一点(强调我的):

除了能够在编译时计算表达式之外,我们还希望能够在编译时要求表达式进行求值; 变量定义前面的constexpr就是这样(并暗示const):

例如:

constexpr int len1 = length("abcd") ;
Run Code Online (Sandbox Code Playgroud)

Bjarne Stroustrup总结了我们何时可以确保此isocpp博客条目中的编译时评估,并说:

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

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

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

  • 也就是说,*原则上*编译器有权查看与`constexpr int x = 5;`内部或没有链接的对象,观察它在编译时不需要该值(假设它没有被使用)作为模板参数或诸如此类的)并实际发出使用5个立即值1和4个加法运算在运行时计算初始值的代码.一个更现实的例子:编译器可能会达到递归限制并将计算延迟到运行时.除非你做了一些迫使编译器实际使用该值的东西,"保证在编译时进行评估"是一个QOI问题. (4认同)
  • @SteveJessop故意阻碍编译器,如臭名昭着(并且不幸的是不存在)Hell ++.这样的事情对于测试一致性/可移植性实际上是很好的. (3认同)
  • 我认为他们都只考虑"自尊的编译器",而不是我所假设的符合标准但故意阻碍的编译器.它可以作为推理标准实际上*保证*的一种手段,而不是其他;-) (2认同)

Ben*_*igt 25

很容易确定对constexpr函数的调用是导致核心常量表达还是仅仅是优化:

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

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

  • 值得一提的是`static_assert(length("str")== 3,"");` (15认同)
  • `constexpr auto test = /*...*/;`可能是最通用和直截了当的. (7认同)
  • ...如果你使用gcc,用`-pedantic`编译.否则,您不会收到任何警告和错误 (4认同)

Max*_*kin 16

请注意,现代编译器(如gcc-4.x)strlen在编译时为字符串文字做,因为它通常被定义为内部函数.未启用优化.虽然结果不是编译时常量.

例如:

printf("%zu\n", strlen("abc"));
Run Code Online (Sandbox Code Playgroud)

结果是:

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


小智 12

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

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

在ideone上查看此示例代码.

  • 由于嵌入的'\ 0',它可能不等于strlen:strlen("hi\0there")!= length("hi\0there") (4认同)

5go*_*der 6

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

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

template<typename T, T V>
struct static_eval
{
  static constexpr T value = V;
};
Run Code Online (Sandbox Code Playgroud)

现在,如果你写

if (static_eval<int, length("hello, world")>::value > 7) { ... }
Run Code Online (Sandbox Code Playgroud)

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

  • 或者只使用std :: integral_constant <int,length(...)> :: value (8认同)

Har*_*rig 6

很简单的:

sizeof("myStringLiteral")做工作。

内部编译器函数sizeof在编译时保证进行评估。它是一个强大的编译器功能,但常常被低估。它适用于 C++ 和 C。

注意:您可能需要从 转换size_t为 int,并减去 1,这两项操作也在编译时完成:

int test_sizeof_text = (int)(sizeof("1234567")-1);

sizeof("text")是包含终止符 0 的大小,因此 -1 表示字符数。