constexpr and consteval Functions in C++20

constexpr and consteval Functions in C++20

This post is a cross-post from www.ModernesCpp.com .

With C++20, constexpr became way more powerful. Additionally, we have?consteval functions in C++20 that are quite similar to constexpr functions.

Let me first describe a feature in C++20 that surprised me the most.

constexpr Containers and Algorithms of the Standard Template Library

C++20 supports the constexpr containers std::vector and std::string, where constexpr means that the member functions of both containers can be applied at compile time. Additionally, the more than

100 classical algorithms of the Standard Template Library are declared as constexpr. Consequently, you can sort a std::vector of ints at compile time.

Let's see what this means:

// constexprVector.cpp

#include <algorithm>
#include <iostream>
#include <vector>

constexpr int maxElement() {
    std::vector myVec = {1, 2, 4, 3};        // (1)
    std::sort(myVec.begin(), myVec.end());
    return myVec.back();
}
int main() {

    std::cout <<  '\n';

    constexpr int maxValue = maxElement();
    std::cout << "maxValue: " << maxValue << '\n';

    constexpr int maxValue2 = [] {
        std::vector myVec = {1, 2, 4, 3};    // (2)
        std::sort(myVec.begin(), myVec.end()) ;
        return myVec.back();
    }(); 

    std::cout << "maxValue2: " << maxValue2 << '\n';

    std::cout << '\n';

}?        

The two containers std::vector (line (1) and (2)) are sorted at compile time using constexpr-declared functions. In the first case, the function maxElement returns the last element of the vector myVec, which is its maximum value. In the second case, I use an immediately-invoked lambda that is declared constexpr. Here is the output of the program:

No alt text provided for this image

The crucial idea for constexpr containers is transient allocation.

Transient Allocation

Transient allocation means that memory allocated at compile time must also be released at compile time. Consequently, the compiler can detect a mismatch of allocation and deallocation in a constexpr function. The following example applies transient allocation.

// transientAllocation.cpp

#include <memory>

constexpr auto correctRelease() {  
     auto* p = new int[2020];
     delete [] p;
     return 2020;
}

constexpr auto forgottenRelease() { // (1)
    auto* p = new int[2020];  
    return 2020;
}

constexpr auto falseRelease() {     // (3)
    auto* p = new int[2020];
    delete p;                       // (2)
    return 2020;
}

int main() {

    constexpr int res1 = correctRelease();
    constexpr int res2 = forgottenRelease();
    constexpr int res3 = falseRelease();

}        

The small program has two serious issues. First, the memory in the constexpr function forgottenRelease (line (1)) is not released. Second, the non-array deallocation (line 3) in the constexpr function falseRelease (line (3)) does not match with the array allocation. Consequentially, the compilation fails.

No alt text provided for this image

With C++20, we got consteval functions that are quite similar to contexpr functions.

consteval Functions

Often developers are irritated because they don't know if a constexpr function is executed at run time or at compile time. Let's consider the following code snippet.

constexpr int constexprFunction(int arg) {
    return arg * arg;
}

static_assert(constexprFunction(10) == 100);                     // (1)
int arrayNewWithConstExpressiomFunction[constexprFunction(100)]; // (2)
constexpr int prod = constexprFunction(100);                     // (3)

int a = 100;
int runTime = constexprFunction(a);                              // (4)

int runTimeOrCompiletime = constexprFunction(100);               // (5)        

constexprFunction is, as its name suggests, a constexpr function.

  1. A constexpr function must run at compile time, when used in a constexpr context or the result is requested at compile time. line (1) and line (2) are constexpr contexts. Line (3), on the contrary, requires the function execution of constexprFuncion on compile time.
  2. The call constexprFunction(a)? (line 4) must be executed at run time because a is not a constant expression.
  3. Line 5 is the interesting case. There are no requirements for the function execution. Therefore, the call constexprFunction(100) (line 5) can be executed at run time or at compile time. From the C++ standard perspective, both is fine.

In contrast to a constexpr function, a consteval function can only be executed at compile time.

consteval creates a so-called immediate function.

consteval int sqr(int n) {
    return n * n;
}        

Each invocation of an immediate function creates a compile-time constant.?consteval cannot be applied to destructors or functions that allocate or deallocate. A consteval function is as a constexpr function implicitly inline and has to fulfill the requirements for a constexpr function.

The requirements of a constexpr function in C++14 and, therefore, a consteval function are:

  • A consteval (constexpr) can
  • have conditional jump instructions or loop instructions.
  • have more than one instruction.
  • invoke constexpr functions. A consteval function can only invoke a constexpr function but not the other way around.
  • use fundamental data types as variables that have to be initialized with a constant expression.
  • A consteval (constexpr) function cannot
  • have static or thread_local data.
  • have a try block nor a goto instruction.
  • invoke or use non-consteval functions or non-constexpr data.

There is one interesting use-case that consteval enables. You can initialize a local non-constant variable at compile time.

// compileTimeInitializationLocal.cpp

consteval auto doubleMe(auto val) {
  return 2 * val;
}

int main() {

auto res = doubleMe(1010);  // (1)
++res;                     // 2021 (2)

}        

The local res is initialized at compile time (line 1) and modified at run time (line 2). On the contrary, if the function doubleMe is declared as constexpr, it could be executed at run time.?

What's next?

Before I dive into the new topic block design with templates, I want to present in the next post the C++17 feature constexpr if. constexpr if?enables it to conditionally compile source code and can also be used for nice tricks at compile time.

"The call constexprFunction(a)?(line 4) must be executed at compile time because a is not a constant expression." that should be run time

回复

要查看或添加评论,请登录

Rainer Grimm的更多文章

  • std::execution: Asynchronous Algorithms

    std::execution: Asynchronous Algorithms

    supports many asynchronous algorithms for various workflows. Presenting proposal P2300R10 is not easy.

  • My ALS Journey (17/n): Christmas Special

    My ALS Journey (17/n): Christmas Special

    Today, I have a special Christmas gift. My ALS Journey so far Make the Difference Let’s do something great together:…

  • std::execution

    std::execution

    std::execution, previously known as executors or Senders/Receivers, provides “a Standard C++ framework for managing…

    1 条评论
  • C++26 Core Language: Small Improvements

    C++26 Core Language: Small Improvements

    There are more small improvements in the C++26 language, which you should know. static_assert extension First, here’s…

  • My ALS Journey (16/n): Good Bye Training / Hello Mentoring

    My ALS Journey (16/n): Good Bye Training / Hello Mentoring

    In 2025, I will no longer offer C++ classes. Instead, I will only offer C++ mentoring in the future.

    1 条评论
  • Placeholders and Extended Character Set

    Placeholders and Extended Character Set

    Placeholders are a nice way to highlight variables that are no longer needed. Additionally, the character set of C++26…

    4 条评论
  • Contracts in C++26

    Contracts in C++26

    Contracts allow you to specify preconditions, postconditions, and invariants for functions. Contracts should already be…

    5 条评论
  • Mentoring as a Key to Success

    Mentoring as a Key to Success

    I know that we are going through very challenging times. Saving is the top priority.

  • Reflection in C++26: Determine the Layout

    Reflection in C++26: Determine the Layout

    Thanks to reflection, you can determine the layout of types. My examples are based on the reflection proposal P2996R5.

    5 条评论
  • My ALS Journey (15/n): A typical Day

    My ALS Journey (15/n): A typical Day

    You may wonder how my day looks. Let me compare a day before ALS with a current day.

    3 条评论

社区洞察

其他会员也浏览了