在编译时计算C字符串的长度。这真的是豪华公寓吗?

时间:2022-07-15 22:28:11

I'm trying to compute the length of a string literal at compile time. To do so I'm using following code:

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

#include <cstdio>

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

int main()
{
    printf("%d %d", length("abcd"), length("abcdefgh"));
}

Everything works as expected, the program prints 4 and 8. The assembly code generated by clang shows that the results are computed at compile time:

一切都按预期运行,程序输出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

My question: is it guaranteed by the standard that length function will be evaluated compile time?

我的问题是:是否有标准保证长度函数将被计算编译时间?

If this is true the door for compile time string literals computations just opened for me... for example I can compute hashes at compile time and many more...

如果这是真的,编译时间字符串字面量计算就为我打开了……例如,我可以在编译时计算散列,以及更多……

6 个解决方案

#1


63  

Constant expressions are not guaranteed to be evaluated at compile time, we only have a non-normative quote from draft C++ standard section 5.19 Constant expressions that says this though:

常量表达式不能保证在编译时进行计算,我们只有从草稿c++标准第5.19节中得到的非规范的引用,它是这样说的:

[...]>[ Note: Constant expressions can be evaluated during translation.—end note ]

[…[注:常数表达式可以在翻译过程中求值。端注)

You can assign the result to constexpr variable to be sure it is evaluated at compile time, we can see this from Bjarne Stroustrup's C++11 reference which says (emphasis mine):

您可以将结果分配给constexpr变量,以确保在编译时对其进行评估,我们可以从Bjarne stroustroustrup的c++ 11引用中看到这一点,其中说(强调我的):

In addition to be able to evaluate expressions at compile time, we want to be able to require expressions to be evaluated at compile time; constexpr in front of a variable definition does that (and implies const):

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

For example:

例如:

constexpr int len1 = length("abcd") ;

Bjarne Stroustrup gives a summary of when we can assure compile time evaluation in this isocpp blog entry and says:

Bjarne Stroustrup总结了什么时候我们可以在isocpp博客中保证编译时间评估,并说:

[...]The correct answer - as stated by Herb - is that according to the standard a constexpr function may be evaluated at compiler time or run time unless it is used as a constant expression, in which case it must be evaluated at compile-time. To guarantee compile-time evaluation, we must either use it where a constant expression is required (e.g., as an array bound or as a case label) or use it to initialize a constexpr. I would hope that no self-respecting compiler would miss the optimization opportunity to do what I originally said: "A constexpr function is evaluated at compile time if all its arguments are constant expressions."

[…[参考译文]正如赫伯所言,正确的答案是,根据标准,constexpr函数可以在编译时或运行时进行计算,除非它用作常量表达式,在这种情况下,它必须在编译时进行计算。为了保证编译时评估,我们必须在需要常量表达式的地方使用它(例如,作为数组绑定或作为case标签),或者使用它初始化constexpr。我希望没有一个自重的编译器会错过这个优化机会来完成我最初所说的:“如果constexpr函数的所有参数都是常量表达式,那么它将在编译时进行计算。”

So this outlines two cases where it should be evaluated at compile time:

所以这两种情况在编译时应该进行评估:

  1. Use it where a constant expression is required, this would seem to be anywhere in the draft standard where the phrase shall be ... converted constant expression or shall be ... constant expression is used, such as an array bound.
  2. 在需要一个常量表达式的地方使用它,这似乎是标准草案中措辞应该是……转换后的常数表达式或应该是…常量表达式被使用,例如数组绑定。
  3. Use it to initialize a constexpr as I outline above.
  4. 使用它来初始化一个constexpr。

#2


24  

It's really easy to find out whether a call to a constexpr function results in a core constant expression or is merely being optimized:

我们很容易发现调用constexpr函数是导致核心常量表达式的结果,还是仅仅是在优化:

Use it in a context where a constant expression is required.

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

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

#3


14  

Just a note, that modern compilers (like gcc-4.x) do strlen for string literals at compile time because it is normally defined as an intrinsic function. With no optimizations enabled. Although the result is not a compile time constant.

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

E.g.:

例如:

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

Results in:

结果:

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

#4


10  

Let me propose another function that computes the length of a string at compile time without being recursive.

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

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

Have a look at this sample code at ideone.

请查看ideone的示例代码。

#5


6  

There is no guarantee that a constexpr function is evaluated at compile-time, though any reasonable compiler will do it at appropriate optimization levels enabled. On the other hand, template parameters must be evaluated at compile-time.

不能保证constexpr函数在编译时进行计算,尽管任何合理的编译器都会在适当的优化级别上执行。另一方面,模板参数必须在编译时进行计算。

I used the following trick to force evaluation at compile time. Unfortunately it only works with integral values (ie not with floating point values).

我使用以下技巧在编译时强制执行计算。不幸的是,它只适用于整体值(不包含浮点值)。

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

Now, if you write

现在,如果你写

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

you can be sure that the if statement is a compile-time constant with no run-time overhead.

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

#6


1  

A short explanation from Wikipedia's entry on Generalized constant expressions:

Wikipedia关于广义常数表达式的条目的简短解释:

The use of constexpr on a function imposes some limitations on what that function can do. First, the function must have a non-void return type. Second, the function body cannot declare variables or define new types. Third, the body may contain only declarations, null statements and a single return statement. There must exist argument values such that, after argument substitution, the expression in the return statement produces a constant expression.

在函数上使用constexpr对函数的功能施加了一些限制。首先,函数必须具有非空返回类型。其次,函数体不能声明变量或定义新类型。第三,正文可能只包含声明、空语句和一个返回语句。必须存在这样的参数值,即在参数替换之后,返回语句中的表达式产生一个常量表达式。

Having the constexpr keyword before a function definition instructs the compiler to check if these limitations are met. If yes, and the function is called with a constant, the returned value is guaranteed to be constant and thus can be used anywhere a constant expression is required.

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

#1


63  

Constant expressions are not guaranteed to be evaluated at compile time, we only have a non-normative quote from draft C++ standard section 5.19 Constant expressions that says this though:

常量表达式不能保证在编译时进行计算,我们只有从草稿c++标准第5.19节中得到的非规范的引用,它是这样说的:

[...]>[ Note: Constant expressions can be evaluated during translation.—end note ]

[…[注:常数表达式可以在翻译过程中求值。端注)

You can assign the result to constexpr variable to be sure it is evaluated at compile time, we can see this from Bjarne Stroustrup's C++11 reference which says (emphasis mine):

您可以将结果分配给constexpr变量,以确保在编译时对其进行评估,我们可以从Bjarne stroustroustrup的c++ 11引用中看到这一点,其中说(强调我的):

In addition to be able to evaluate expressions at compile time, we want to be able to require expressions to be evaluated at compile time; constexpr in front of a variable definition does that (and implies const):

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

For example:

例如:

constexpr int len1 = length("abcd") ;

Bjarne Stroustrup gives a summary of when we can assure compile time evaluation in this isocpp blog entry and says:

Bjarne Stroustrup总结了什么时候我们可以在isocpp博客中保证编译时间评估,并说:

[...]The correct answer - as stated by Herb - is that according to the standard a constexpr function may be evaluated at compiler time or run time unless it is used as a constant expression, in which case it must be evaluated at compile-time. To guarantee compile-time evaluation, we must either use it where a constant expression is required (e.g., as an array bound or as a case label) or use it to initialize a constexpr. I would hope that no self-respecting compiler would miss the optimization opportunity to do what I originally said: "A constexpr function is evaluated at compile time if all its arguments are constant expressions."

[…[参考译文]正如赫伯所言,正确的答案是,根据标准,constexpr函数可以在编译时或运行时进行计算,除非它用作常量表达式,在这种情况下,它必须在编译时进行计算。为了保证编译时评估,我们必须在需要常量表达式的地方使用它(例如,作为数组绑定或作为case标签),或者使用它初始化constexpr。我希望没有一个自重的编译器会错过这个优化机会来完成我最初所说的:“如果constexpr函数的所有参数都是常量表达式,那么它将在编译时进行计算。”

So this outlines two cases where it should be evaluated at compile time:

所以这两种情况在编译时应该进行评估:

  1. Use it where a constant expression is required, this would seem to be anywhere in the draft standard where the phrase shall be ... converted constant expression or shall be ... constant expression is used, such as an array bound.
  2. 在需要一个常量表达式的地方使用它,这似乎是标准草案中措辞应该是……转换后的常数表达式或应该是…常量表达式被使用,例如数组绑定。
  3. Use it to initialize a constexpr as I outline above.
  4. 使用它来初始化一个constexpr。

#2


24  

It's really easy to find out whether a call to a constexpr function results in a core constant expression or is merely being optimized:

我们很容易发现调用constexpr函数是导致核心常量表达式的结果,还是仅仅是在优化:

Use it in a context where a constant expression is required.

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

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

#3


14  

Just a note, that modern compilers (like gcc-4.x) do strlen for string literals at compile time because it is normally defined as an intrinsic function. With no optimizations enabled. Although the result is not a compile time constant.

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

E.g.:

例如:

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

Results in:

结果:

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

#4


10  

Let me propose another function that computes the length of a string at compile time without being recursive.

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

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

Have a look at this sample code at ideone.

请查看ideone的示例代码。

#5


6  

There is no guarantee that a constexpr function is evaluated at compile-time, though any reasonable compiler will do it at appropriate optimization levels enabled. On the other hand, template parameters must be evaluated at compile-time.

不能保证constexpr函数在编译时进行计算,尽管任何合理的编译器都会在适当的优化级别上执行。另一方面,模板参数必须在编译时进行计算。

I used the following trick to force evaluation at compile time. Unfortunately it only works with integral values (ie not with floating point values).

我使用以下技巧在编译时强制执行计算。不幸的是,它只适用于整体值(不包含浮点值)。

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

Now, if you write

现在,如果你写

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

you can be sure that the if statement is a compile-time constant with no run-time overhead.

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

#6


1  

A short explanation from Wikipedia's entry on Generalized constant expressions:

Wikipedia关于广义常数表达式的条目的简短解释:

The use of constexpr on a function imposes some limitations on what that function can do. First, the function must have a non-void return type. Second, the function body cannot declare variables or define new types. Third, the body may contain only declarations, null statements and a single return statement. There must exist argument values such that, after argument substitution, the expression in the return statement produces a constant expression.

在函数上使用constexpr对函数的功能施加了一些限制。首先,函数必须具有非空返回类型。其次,函数体不能声明变量或定义新类型。第三,正文可能只包含声明、空语句和一个返回语句。必须存在这样的参数值,即在参数替换之后,返回语句中的表达式产生一个常量表达式。

Having the constexpr keyword before a function definition instructs the compiler to check if these limitations are met. If yes, and the function is called with a constant, the returned value is guaranteed to be constant and thus can be used anywhere a constant expression is required.

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