Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
josuttis_nm_c20_the_complete_guide.pdf
Скачиваний:
44
Добавлен:
27.03.2023
Размер:
5.85 Mб
Скачать

18.5 Using Heap Memory, Vectors, and Strings at Compile Time

625

By using the view adaptor std::views::counted(), we can easily combine both the returned array and the returned size of elements to use in the array as a single range.

The output of the program is now:

0 3 4 8 15 42 77 132

18.5.3 Using Strings at Compile Time

For compile-time strings, all operations are constexpr now. Therefore, you can use std::string but also any other string types such as std::u8string at compile time now.

However, there is again the restriction that you cannot use a compile-time string at runtime. For example:

consteval std::string returnString()

{

std::string s = "Some string from compile time";

...

return s;

}

 

void useString()

 

{

 

constexpr auto s = returnString();

// ERROR

...

 

}

 

constexpr void useStringInConstexpr()

 

{

 

std::string s = returnString();

// ERROR

...

 

}

 

consteval void useStringInConsteval()

 

{

 

std::string s = returnString();

// OK

...

 

}

 

You also cannot solve the problem by returning just the compile-time string with data() or c_str() or as a std::string_view. You would return the address of memory allocated at compile time. Compilers raise a compile-time error if that happens.

However, we can use the same trick as described for vectors above. We can convert the string into an array of fixed size and return both the array and the size of the vector.

626

Chapter 18: Compile-Time Computing

Here is a complete example:

comptime/comptimestring.cpp

#include <iostream> #include <string> #include <array> #include <cassert> #include "asstring.hpp"

// function template to export a compile-time string to runtime: template<int MaxSize>

consteval auto toRuntimeString(std::string s)

{

//ensure the size of the exported array is large enough: assert(s.size() <= MaxSize);

//create a compile-time array and copy all characters into it:

std::array<char, MaxSize+1> arr{}; // ensure all elems are initialized for (int i = 0; i < s.size(); ++i) {

arr[i] = s[i];

}

// return the compile-time array and the string size: return std::pair{arr, s.size()};

}

// function to import an exported compile-time string at runtime: std::string fromComptimeString(const auto& dataAndSize)

{

// init string with exported array of chars and size: return std::string{dataAndSize.first.data(),

dataAndSize.second};

}

// test the functions:

consteval auto comptimeMaxStr()

{

std::string s = "max int is " + asString(std::numeric_limits<int>::max())

+" (" + asString(std::numeric_limits<int>::digits + 1)

+" bits)";

return toRuntimeString<100>(s);

}

18.5 Using Heap Memory, Vectors, and Strings at Compile Time

627

 

 

 

int main()

 

 

 

 

 

 

 

 

{

 

 

 

 

std::string s = fromComptimeString(comptimeMaxStr());

 

 

 

 

std::cout << s << '\n';

 

 

 

 

 

 

 

}

 

 

 

Again, we define two helper functions to export a compile-time string to a runtime string:

The compile-time function toRuntimeString() converts a string into a std::array<> and returns the array and the size of the string:

template<int MaxSize>

consteval auto toRuntimeString(std::string s)

{

assert(s.size() <= MaxSize);

// ensure array size fits

// create a compile-time array and copy all characters into it:

std::array<char, MaxSize+1> arr{};

// ensure all elems are initialized

for (int i = 0; i < s.size(); ++i) {

 

arr[i] = s[i];

 

}

 

return std::pair{arr, s.size()};

// return array and size

}

By using assert(), we double check that the size of the array is large enough. In the same way, we could double check at compile time that we do not waste too much memory.

The runtime function fromComptimeString() then takes the returned array and size to initialize a runtime string and returns it:

std::string fromComptimeString(const auto& dataAndSize)

{

return std::string{dataAndSize.first.data(), dataAndSize.second};

}

The test case of the function uses the helper function asString(), which can be used at compile time and runtime to convert an integral value into a string.

The program has, for example, the following output:

max int is 2147483647 (32 bits)

628

Chapter 18: Compile-Time Computing

18.6Other constexpr Extensions

Besides the ability to use heap memory at compile time, compile-time functions (whether declared with constexpr or consteval) can use a couple of additional language features since C++20. As a consequence and in addition, a couple of library features can now also be used at compile time.

18.6.1 constexpr Language Extensions

Since C++20, the following language features can be used in compile-time functions (whether declared with constexpr or consteval):

You can now use heap memory at compile time.

Runtime polymorphism is supported:

You can now use virtual functions.

You can now use dynamic_cast.

You can now use typeid.

You can have try-catch blocks now (but you are still not allowed to throw).

You can now change the active member of a union.

Note that you are still not allowed to use static in constexpr or consteval functions.

18.6.2 constexpr Library Extensions

The C++ standard library has extended the utilities that can be used at compile time.

constexpr Algorithms and Utilities

Most algorithms in <algorithm>, <numeric>, and <utility> are constexpr now. This means that you can now sort and accumulate elements at compile time (see the example using vectors at compile time).

However, parallel algorithms (algorithms that have an execution policy parameter) can still only be used at runtime.

constexpr Library Types

Several types of the C++ standard library now have better support for constexpr so that objects can be used (better) at compile time:

Vectors and strings can be used at compile time now.

A couple of std::complex<> operations became constexpr.

A couple of missing constexpr in std::optional<> and std::variant<> were added.

constexpr was added to std::invoke(), std::ref(), std::cref(), mem_fn(), not_fn(), std::bind(), and std::bind_front(),

In pointer_traits, pointer_to() for raw pointers can now be used at compile time.