constexpr std::vector and std::string in C++20

Contents[Show]

Probably the most viral keyword in modern C++ is constexpr. With C++20, we have a constexpr std::vector and a constexpr std::string. Additionally, both containers can be manipulated with the constexpr algorithms of the Standard Template Library. 

 

TimelineCpp20CoreLanguage

In this post, I want to calculate the sum and the product of a few numbers at compile time. Depending on the applied C++ standard, this is quite challenging or quite comfortable to achieve. Let's start with C++11.

Variadic Templates in C++11

A variadic template is a template that can be invoked with an arbitrary number of arguments. By using the ellipse (...) tails becomes a so-called parameter pack. Parameter packs can only be packed and unpacked. If the ellipse is left from tails, the parameter pack is packed. If the ellipse is right from tails, the parameter pack is unpacked. 

 

// compiletimeVariadicTemplates.cpp

#include <iostream>

template<int...>
struct sum;

template<>
struct sum<> {
  static constexpr int value = 0;
};

template<int i, int... tail>
struct sum<i, tail...> {
  static constexpr int value = i + sum<tail...>::value;
};

template<int...>                                  // (1)
struct product;

template<>                                        // (2)                                 
struct product<> {
  static constexpr int value = 1;
};

template<int i, int... tail>                      // (3)
struct product<i, tail...> {
  static constexpr int value = i * product<tail...>::value;
};


int main() {

    std::cout << std::endl;

    std::cout << "sum<1, 2, 3, 4, 5>::value: " << sum<1, 2, 3, 4, 5>::value << std::endl;
    std::cout << "product<1, 2, 3, 4, 5>::value: " << product<1, 2, 3, 4, 5>::value << std::endl;

    std::cout << std::endl;

}

 

The program calculates the sum and the product of the numbers 1 to 5 at compile time. In the case of the function template product, line (1) declares the primary template, line (2) the full specialization for zero arguments, and line (3) the partial specialization for at least one argument. The definition of the primary template (1) is not necessary if you don't use it. The partial specialization (3) starts a recursive instantiation, which ends when all template arguments are consumed. In this case, the full specialization for zero arguments kicks in as the boundary condition. If you want to know how this pack expansion is performed, study the example compileTimeVariadicTemplates.cpp at C++ Insights.

compileTimeVariadicTemplate

Thanks to fold expression, calculating at compile-time becomes way easier.

Fold Expressions in C++17

With C++17 we can directly reduce a parameter pack with a binary operator.

 

// compiletimeFoldExpressions.cpp

#include <iostream>

template<typename... Args>
auto sum(const Args&... args)
{
  return (args + ...);
}

template<typename... Args>
auto product(const Args&... args)
{
  return (args * ...);
}

int main() {

    std::cout << std::endl;

    std::cout << "sum(1, 2, 3, 4, 5): " << sum(1, 2, 3, 4, 5) << std::endl;
    std::cout << "product(1, 2, 3, 4, 5): " << product(1, 2, 3, 4, 5) << std::endl;

    std::cout << std::endl;

}

 

The program compiletimeFoldExpressions.cpp produces the same result at compile-time, such as the previous program  compileTimeVariadicTemplates.cpp.

compiletimeFoldExpressions

Of course, there is more to write about fold expression. Read the details in my previous post to Fold Expressions.

Now I want to dive into C++20.

constexpr Containers and Algorithms in C++20

C++20 supports the constexpr containers std::vector and std::string. constexpr means in this case, that the member functions of both containers can be applied at compile-time.

Before I write about both containers, I have to make a short detour to C++17. The reason is simple: No compiler so far supports the constexpr std::vector and std::string. In contrast, the GCC and the Microsoft Compiler support the constexpr algorithms of the STL.

In my following example, I use instead of a constexpr std::vector a constexpr std::array. Since C++17 std::array can be declared as constexpr: constexpr std::array myArray{1, 2, 3}.

Now starts the fun part. With C++20, you can use a std::array at compile-time.

// constexprArray.cpp

#include <iostream>
#include <numeric>
#include <array>


int main() {

    std::cout << std::endl;

    constexpr std::array myArray{1, 2, 3, 4, 5};                                     // (1)
    constexpr auto sum = std::accumulate(myArray.begin(), myArray.end(), 0);         // (2)
    std::cout << "sum: "  << sum << std::endl;

    constexpr auto product = std::accumulate(myArray.begin(), myArray.end(), 1,      // (3)
std::multiplies<int>()); std::cout << "product: " << product << std::endl; constexpr auto product2 = std::accumulate(myArray.begin(), myArray.end(), 1, // (4)
[](auto a, auto b) { return a * b;}); std::cout << "product2: " << product2 << std::endl; std::cout << std::endl; }

 

The std::array (1) and all results of the calculations are declared as constexpr. Line (2) calculates the sum of all elements, and the lines (3) and (4) calculate the product of all elements of myArray. Line 2 is valid because myArray is a constexpr container, and the algorithm std::accumulate is declared as constexpr. Line (3) and (4) are more interesting. The call operator of std::multiplies is constexpr and since C++17 lambda expression can be constexpr.

Here is the output of the program:

constexprArray

Thanks to the Compiler Explorer, I can present to you the results way more impressive. Here are the relevant assembler instructions of GCC.

constexprArrayCompilerExplorer

 

Lines 19, 29, and 39 show that the results of the array calculations become values in the assembler instructions.  This means std::accumulate is performed at compile-time and the result of the calculation is just available at run-time.

As I already mentioned it, no compiler so far supports a constexpr std::vector or a constexpr std::string. Consequently, I have to cheat and assume that my compiler fully supports both constexpr containers.

 

// constexprVectorString.cpp

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

int main() {

    std::cout << std::endl;

    constexpr std::vector myVec {15, -5, 0, 5, 10};
    constexpr std::sort(myVec.begin(), myVec.end());
    for (auto v: myVec) std::cout << v << " ";
    std::cout << "\n\n";

    using namespace std::string_literals;
    constexpr std::vector<std::string> myStringVec{"Stroustrup"s, "Vandevoorde"s, 
"Sutter"s, "Josuttis"s, "Wong"s }; constexpr std::sort(myStringVec.begin(), myStringVec.end()); for (auto s: myStringVec) std::cout << s << " "; std::cout << "\n\n"; }

 

With C++20, you can sort a std::vector or a std::string at compile-time.

constexprVectorString

What's next?

C++20 adds many convenience functions to make it easier to work with containers of the Standard Template Library. For example, due to the functions std::erase and std::erase_if, deletion of elements of a container works like a charm. When you want to know if a specific element is in an associative container, the member function contains is quite handy.

 

 

 

Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Venkata Ramesh Gudpati, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Darshan Mody, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Wolfgang Gärtner, Jon Hess, Christian Wittenhorst, Louis St-Amour, Stephan Roslen, Venkat Nandam, Jose Francisco, Douglas Tinkham, Lakshman, Kuchlong Kuchlong, Avi Kohn, Serhy Pyton, Robert Blanch, Kuma [], Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, Kai, and Sudhakar Balagurusamy.

 

 

Seminars

I'm happy to give online-seminars or face-to-face seminars world-wide. Please call me if you have any questions.

Standard Seminars 

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

Contact Me

Modernes C++,

RainerGrimmSmall

Comments   

0 #1 PascalM 2020-09-21 20:24
I may be wrong but I think that your example of constexpr vector is not possible even in C++20.
- We will not be able to define a constexpr vector.
- We will not be able to sort a constexpr vector after its definition (this would be weird as a constexpr object is implicitely const).
What we will be able to do is to use vector inside a constexpr function for an intermediate computation.
Quote

My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 2258

Yesterday 7957

Week 34780

Month 189375

All 4810269

Currently are 235 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments