Computing length of a C string at compile time. Is this really a constexpr?

浪尽此生 提交于 2019-11-26 17:27:26

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:

[...]>[ 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):

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):

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:

[...]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."

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. Use it to initialize a constexpr as I outline above.

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:

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;
}

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.

E.g.:

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

Results in:

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

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.

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.

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.

kaedinger

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

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.

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!