** In this article, we will introduce the use of STD ::array from various perspectives, hoping to bring some inspiration.

Td ::array is an STL container added to the C++11 standard, designed to provide similar functionality and performance to native arrays. This makes STD :: Array different from other containers. For example, STD :: Array elements are stored directly within the instance rather than allocated space on the heap. The size of STD ::array must be determined at compile time; STD :: Array’s constructors, destructors, and assignment operators are all implicitly declared by the compiler… This makes STD ::array difficult to use for programmers who are used to containers like STD ::vector. But in reality, the power of STD :: Array is probably underestimated. In this article, I will introduce the use of STD :: Array from various perspectives and hopefully provide some inspiration.

The code in this article is compiled and run in C++17. The current mainstream g++ versions already support the C++17 standard, but many versions (such as GCC 7.3) do not have the C++17 feature turned on by default and require manual compilation options **-std= C++17. **

Automatically derive array sizes

Many projects have global arrays like this as configuration parameters:

uint32_t g_cfgPara[] = {1, 2, 5, 6, 7, 9, 3, 4};

The trouble comes when programmers want to replace native arrays with STD ::array:

array<uint32_t, 8> g_cfgPara = {1, 2, 5, 6, 7, 9, 3, 4}; // Note the template parameter “8”

The programmer had to write out the size of the array by hand because it is one of the STD ::array template parameters. If the array is long, or if members are frequently added or deleted, maintaining the array size may not be so pleasant. The STD ::array declaration is less convenient to use than the native array declaration. However, this complaint was limited to C++17, which introduced the class template argument derivation feature, and you no longer had to manually specify class template parameters:

array g_cfgPara = {1, 2, 5, 6, 7, 9, 3, 4}; // Array size and member type are automatically derived

It looks nice, but someone soon notices something is wrong: What is the type of the array element? Still STD :: Uint32_t? Some people started trying to just supply element type arguments and let the compiler derive the length automatically, but unfortunately it didn’t work.

array<uint32_t> g_cfgPara = {1, 2, 5, 6, 7, 9, 3, 4}; // Error compiling

Well, for the moment, it looks like STD ::array can’t be declared like a native array. So let’s solve this problem.

Return STD ::array with the function

The idea is to replace class templates with function templates — since C++ allows some of the parameters of function templates to be automatically derived — with helper functions like STD ::make_pair and STD ::make_tuple. Coincidentally, the C++ standard did introduce STD ::make_array in experimental versions of TS v2, but this utility function was later dropped due to the advent of class template argument derivations. But clearly, the demand is there. In C++20, a helper function STD ::to_array was added. Don’t be intimidated by C++20, the code for this function is simple enough to take and define it in your own C++17 code [1].

template<typename R, typename P, size_t N, size_t… I> constexpr array<R, N> to_array_impl(P (&a)[N], std::index_sequence<I… >) noexcept { return { {a[I]… }}; }

template<typename T, size_t N> constexpr auto to_array(T (&a)[N]) noexcept { return to_array_impl<std::remove_cv_t, T, N>(a, std::make_index_sequence{}); }

template<typename R, typename P, size_t N, size_t… I> constexpr array<R, N> to_array_impl(P (&&a)[N], std::index_sequence<I… >) noexcept { return { {move(a[I])… }}; }

template<typename T, size_t N> constexpr auto to_array(T (&&a)[N]) noexcept { return to_array_impl<std::remove_cv_t, T, N>(move(a), std::make_index_sequence{}); }

Careful friends will notice that this definition is purposefully different from the recommended implementation of C++20. I’ll explain why later.

Now let’s try a new approach to an old problem:

auto g_cfgPara = to_array({1, 2, 5, 6, 7, 9, 3, 4}); // The type is not uint32_t?

Uint32_t = STD ::uint32_t This is because the template argument derivation rejects implicit conversions for STD :: Initializer_list elements, and if you change the to_array template argument from int to uint32_t, you get the following compilation error:

D:WorkSource_CodesMyProgramVSCodemain.cpp:51:61: error: no matching function for call to ‘to_array<uint32_t>()’ auto g_cfgPara = to_array<uint32_t>({1, 2, 5, 6, 7, 9, 3, 4}); D:WorkSource_CodesMyProgramVSCodemain.cpp:34:16: note: candidate: ‘template<class T, long long unsigned int N> constexpr auto to_array(T (&)[N])’ constexpr auto to_array(T (&a)[N]) noexcept ^~~~~~~~ D:WorkSource_CodesMyProgramVSCodemain.cpp:34:16: note: template argument deduction/substitution failed: D:WorkSource_CodesMyProgramVSCodemain.cpp:51:61: note: mismatched types ‘unsigned int’ and ‘int’ auto g_cfgPara = to_array<uint32_t>({1, 2, 5, 6, 7, 9, 3, 4}); D:WorkSource_CodesMyProgramVSCodemain.cpp:46:16: note: candidate: ‘template<class T, long long unsigned int N> constexpr auto to_array(T (&&)[N])’ constexpr auto to_array(T (&&a)[N]) noexcept ^~~~~~~~ D:WorkSource_CodesMyProgramVSCodemain.cpp:46:16: note: template argument deduction/substitution failed: D:WorkSource_CodesMyProgramVSCodemain.cpp:51:61: note: mismatched types ‘unsigned int’ and ‘int’

Hoho, it’s kind of sad no, it goes all the way back to the origin, and it still can’t force the type. This is where the previous change to STD ::array comes in: I added a template parameter to to_array_IMPl to make the input array elements and the elements that return STD ::array different type parameters, thus opening up the possibility of type conversion. To convert to the specified type, we also need to add two utility functions:

template<typename R, typename P, size_t N> constexpr auto to_typed_array(P (&a)[N]) noexcept { return to_array_impl<R, P, N>(a, std::make_index_sequence{}); }

template<typename R, typename P, size_t N> constexpr auto to_typed_array(P (&&a)[N]) noexcept { return to_array_impl<R, P, N>(move(a), std::make_index_sequence{}); }

The difference between these two functions and to_array is that it takes three template arguments: the first is the element type of STD ::array to return, and the last two are the same as to_array. This allows us to implement the custom STD :: Array element type by specifying the first parameter.

auto g_cfgPara = to_typed_array<uint32_t>({1, 2, 5, 6, 7, 9, 3, 4}); // Automatically convert elements to uint32_t

This code compiles and runs, but there are compiler alarms for type conversions. Of course, if you’re brave enough, you can put a static_cast in the to_array_impl function to clear the alarm. But the compile alarm tells us something we can’t ignore: what if the input value overflows?

auto g_a = to_typed_array<uint8_t>({256, -1}); // Numbers out of the uint8_t range

The compiler will do the same thing and it will let you compile and run, and the two elements in G_A will have values of 0 and 255. If you don’t understand why these two values are different from input parameters, it’s time to review your knowledge of integer overflow and wrap. Clearly, the plan is not perfect. But we can keep improving.

Compile-time literal numeric validity verification

The first thing that comes to mind is to put an if statement or something like that in the to_array_impl function, throw an exception or do something else with input that is outside the target value range. This is certainly possible, but it is important to note that these utility functions can be called at run time, and performance is critical for such common base functions. Once you add false judgments, it means that every call at run time will degrade performance. Ideally, only arrays that are generated at compile time are checked, and compile errors are reported. But do not add any validation when calling a function at runtime. Unfortunately, at least until C++20, there was no way to specify that functions were only allowed to be executed at compile time [2]. Is there another way? Those familiar with C++ know that most of the compile-time processing in C++ can be done with trick in templates — template parameters must be compile-time constants. So we can use template arguments for compile-time processing — as long as the array elements are all nontype arguments to the template. Of course, there is a problem: how do we determine the types of the template’s non-type parameters? C++17 provides the function of the auto template parameter to come in handy:

Template constexpr void CheckIntRanges() noexcept {} template<typename T, auto M, auto… N> constexpr void CheckIntRanges() noexcept {// To prevent unsigned vs. signed comparisons static_assert(! ((std::numeric_limits::min() >= 0) && (M < 0))); / / range checking static_assert ((M > = STD: : numeric_limits: : min () && (M < = STD: : numeric_limits: : Max ()));

CheckIntRanges<T, N... > ();Copy the code

}

template<typename T, auto… N> constexpr auto DeclareArray() noexcept { CheckIntRanges<T, N… > (); array<T, sizeof… (N)> a{{static_cast(N)… }}; return a; };

Note that in this function, all validation is done with static_assert. This ensures that validation only occurs at compile time and incurs no runtime overhead. DeclareArray is used as follows:

constexpr auto a1 = DeclareArray<uint8_t, 1, 2, 3, 4, 255>(); // Declare a STD ::array<uint8_t, 5> with 1, 2, 3, 4, 255 static_assert(a1.size() == 5); static_assert(a1[3] == 4); auto a2 = DeclareArray<uint8_t, 1, 2, 3, -1>(); Auto A3 = DeclareArray<uint16_t, 1, 2, 3, 65536>(); // compilation error, 65536 out of uint16_t range

Here’s a misconception: some people might declare DeclareArray as:

template<typename T, T… N> // Note that N is of type T constexpr Auto DeclareArray() noexcept

If you do this, you will find that the validation of the value always passes — because the template parameter is already converted to type T before entering the validation. If your compiler doesn’t support the C++17 auto template argument, you can do this indirectly by using STD ::uint64_t, STD ::int64_t and other “Max” types. Another point to note is that C++ has restrictions on the types allowed for untyped template arguments, and DeclareArray methods can only be used in cases where array elements are primitively typed (at least until C++20). But it was enough. If the elements of the array are of a custom type, you can control the conversion through methods such as custom constructors. If this is interesting to you, then you’re right. There’s more to come.

Generates arrays at compile time

The constexpr modifier added to C++11 can do a lot of computations at compile time. But a general constexpr function can only return a single value, and if you want to use it to return a collection of objects, you get into trouble: STL containers have dynamic memory requirments and can’t be used as compile-time constants (at least until C++20); The return value of a native array degrades to a pointer, resulting in a dangling pointer being returned. Even references that return arrays do not work, resulting in dangling references.

constexpr int* Func() noexcept { int a[] = {1, 2, 3, 4}; return a; // Serious error! Returns the address of the local object}

It wasn’t until the advent of STD :: Array that the problem was solved. STD ::array can be either a compile-time constant or a function return value. As a result, it becomes the first choice for returning collection data at compile time. We have seen how to return an array at compile time in the implementation of utility functions such as to_array above. Let’s be a little more bold here and write a compile-time bubble sort:

template<typename T, size_t N> constexpr std::array<T, N> Sort(const std::array<T, N>& numbers) noexcept { std::array<T, N> sorted(numbers); for (int i = 0; i < N; ++i) { for (int j = N – 1; j > i; –j) { if (sorted[j] < sorted[j – 1]) { T t = sorted[j]; sorted[j] = sorted[j – 1]; sorted[j – 1] = t; } } } return sorted; } int main() { constexpr std::array<int, 4> before{4, 2, 3, 1}; constexpr std::array<int, 4> after = Sort(before); static_assert(after[0] == 1); static_assert(after[1] == 2); static_assert(after[2] == 3); static_assert(after[3] == 4); return 0; }

Since the whole sorting algorithm is done at compile time, there is no need to worry too much about the efficiency of bubble sorting. Of course, you can write a compile-time quicksort if you want — constexpr can also be used at runtime, so it’s doubtful that one of your Hanes will call it at runtime. There are two things to note when writing constexpr functions:

1. Non-constexpr functions cannot be called from constExpr functions. Therefore, you cannot swap elements with STD ::swap, nor can you call STD ::sort directly for sorting.

  1. The array passed in is constExpr, so the parameter type must be const, and the data cannot be sorted in place. A new array must be returned.

Despite the limitations, the benefits of compile-time algorithms are huge: if the operation has undefined behavior such as an out-of-bounds array, the compilation will fail. A compile-time test for a CONSTEXPr function can effectively intercept problems earlier than a run-time test. And as long as the compile passes, the test passes, which is much more convenient than running extra white-box test cases. The list of static_assert statements above is uncomfortable. The reason for this is that the operator== function of STD ::array is not constexpr (at least not until C++20). But we can also define a template function of our own to determine whether two arrays are equal:

template<typename T, typename U, size_t M, size_t N> constexpr bool EqualsImpl(const T& lhs, const U& rhs) { static_assert(M == N); for (size_t i = 0; i < M; ++i) { if (lhs[i] ! = rhs[i]) { return false; } } return true; }

template<typename T, typename U> constexpr bool Equals(const T& lhs, const U& rhs) { return EqualsImpl<T, U, size(lhs), size(rhs)>(lhs, rhs); }

template<typename T, typename U, size_t N> constexpr bool Equals(const T& lhs, const U (&rhs)[N]) { return EqualsImpl<T, const U (&)[N], size(lhs), N>(lhs, rhs); } int main() { constexpr std::array<int, 4> before{4, 2, 3, 1}; constexpr std::array<int, 4> after = Sort(before); static_assert(Equals(after, {1, 2, 3, 4})); // Compare STD ::array with the native array static_assert(! Equals(before, after)); // Compare two STD ::array return 0; }

We define Equals, which is more powerful than the STD ::array comparison operator, and can even compare STD ::array with native arrays. There are two points to note about Equals:

1. STD ::size is a utility function provided by C++17 that returns the size of various containers and arrays. Of course, Equals will only allow compile-time sized containers to be passed in, otherwise a compile failure will be triggered.

2. Equals defines two versions, which are forced by a C++ restriction: C++ forbids {… } The STD :: Initializer_list literal is derived as a template parameter type, so we must provide a version declaring the parameter type as array so that **{1, 2, 3, 4}** can be passed in as an argument. Compile-time sorting is an instructive attempt to generate other compile-time set constants in a similar way, such as sequences of natural numbers of specified length:

template<typename T, size_t N> constexpr auto NaturalNumbers() noexcept { array<T, N> arr{0}; For (size_t I = 0; i < N; ++i) { arr[i] = i + 1; } return arr; } int main() { constexpr auto arr = NaturalNumbers<uint32_t, 5>(); static_assert(Equals(arr, {1, 2, 3, 4, 5})); return 0; }

This code compiles and runs without problems, but it is not recommended. The reason is that the NaturalNumbers function defines a local array of all zeros and then modifies its values one by one. This is more efficient than returning an array with the specified value directly. One might wonder if we could remove the initialization from arR, but this would result in a compilation error — a constEXPr function is not allowed to define uninitialized local variables.

One might think that these calculations are compile-time and have no impact on runtime efficiency — but don’t forget that constEXPr functions can also be called at runtime. A better way to do this is to use the to_array function above to initialize the array in one go, without having to assign values one by one.

Using this new idea, we write a general-purpose array generator that takes a function object as an argument and calls that function object to generate the value of each element of the array. The following code also demonstrates how to use this generator to generate odd and Fibonacci sequences at compile time.

template constexpr T OddNumber(size_t i) noexcept { return i * 2 + 1; }

template constexpr T Fibonacci(size_t i) noexcept { if (i <= 1) { return 1; } return Fibonacci(i – 1) + Fibonacci(i – 2); }

template<typename T, size_t N, typename F, size_t… I> constexpr array<std::remove_cv_t, N> GenerateArrayImpl(F f, std::index_sequence<I… >) noexcept { return { {f(I)… }}; }

template<size_t N, typename F, typename T = invoke_result_t<F, size_t>> constexpr array<T, N> GenerateArray(F f) noexcept { return GenerateArrayImpl<T, N>(f, std::make_index_sequence{}); } int main() { constexpr auto oddNumbers = GenerateArray<5>(OddNumber<uint8_t>); static_assert(Equals(oddNumbers, {1, 3, 5, 7, 9})); constexpr auto fiboNumbers = GenerateArray<5>(Fibonacci<uint32_t>); static_assert(Equals(fiboNumbers, {1, 1, 2, 3, 5})); Constexpr auto specified = GenerateArray<3>([](size_t I) {return I + 10; // lambda can even be passed in to customize the numeric sequence to be generated (qualified C++17) constexpr auto specified = GenerateArray<3>([](size_t I) {return I + 10; }); static_assert(Equals(specified, {10, 11, 12})); return 0; }

That last one, passing lambda to customize the array, raises a question: is lambda a constexpr function? The answer is: yes, but you need C++17 support. GenerateArray is an array generator that will play a big role in the future, so keep reading.

Intercepts subarrays

STD ::array does not provide a constructor that inputs a specified interval to create a new container, but with the array generator above, we can write an auxiliary function to implement subarray generation (again using the lambda function as the generation algorithm).

template<size_t N, typename T> constexpr auto SubArray(T&& t, size_t base) noexcept { return GenerateArray([base, t = forward(t)](size_t i) { return t[base + i]; }); }

template<size_t N, typename T, size_t M> constexpr auto SubArray(const T (&t)[M], size_t base) noexcept { return GenerateArray([base, &t](size_t i) { return t[base + i]; }); } int main() {constexpr auto x = SubArray<3>({1, 2, 3, 4, 5, 6}, 2); Static_assert (Equals(x, {3, 4, 5})); Constexpr auto x1 = SubArray<2>(x, 1); constexpr auto x1 = SubArray<2>(x, 1); Static_assert (Equals(x1, {4, 5})); // ConstEXPr uint8_t a[] = {9, 8, 7, 6, 5}; constexpr auto y = SubArray<2>(a, 3); static_assert(Equals(y, {6, 5})); Const char* STR = “Hello world!” const char* STR = “Hello world!” ; constexpr auto z = SubArray<5>(str, 6); static_assert(Equals(z, {‘w’, ‘o’, ‘r’, ‘l’, ‘d’})); Vector <int32_t> v{10, 11, 12, 13, 14}; vector<int32_t> v{10, 11, 12, 13, 14}; size_t n = 2; auto d = SubArray<3>(v, n); Assert (d, {12, 13, 14})); // Note that static_assert is not a compile-time constant return 0; }

With SubArray, the template argument N is the size of the SubArray to truncate, the input parameter T is any type that supports subscripting, and the input parameter base is the starting position of the truncated element. Since the size of STD ::array is determined at compile time, N must be a compile-time constant, but the base argument can be a runtime variable.

When all input parameters are compile-time constants, the resulting subarray is also compile-time constants.

SubArray is provided in two versions, also so that the STD :: Initializer_list literal can be passed in as a parameter.

Concatenating multiple arrays

Multiple arrays can be concatenated in a similar way, again using lambda as the generating function.

template constexpr auto TotalLength(const T& arr) noexcept { return size(arr); }

template<typename P, typename… T> constexpr auto TotalLength(const P& p, const T&… arr) noexcept { return size(p) + TotalLength(arr…) ; }

template constexpr auto PickElement(size_t i, const T& arr) noexcept { return arr[i]; }

template<typename P, typename… T> constexpr auto PickElement(size_t i, const P& p, const T&… arr) noexcept { if (i < size(p)) { return p[i]; } return PickElement(i – size(p), arr…) ; }

template<typename… T> constexpr auto ConcatArrays(const T&… arr) noexcept { return GenerateArray<TotalLength(arr…) >([&arr…] (size_t i) { return PickElement(i, arr…) ; }); } int main() { constexpr int32_t a[] = {1, 2, 3}; // ConstEXPr auto b = to_typed_array<int32_t>({4, 5, 6}); // std::array constexpr auto c = DeclareArray<int32_t, 7, 8>(); // std::array constexpr auto x = ConcatArrays(a, b, c); Static_assert (Equals(x, {1, 2, 3, 4, 5, 6, 7, 8})); return 0; }

As before, ConcatArrays uses template parameters to be compatible with both native and STD :: Array, and it even accepts any compile-time length of custom types for concatenation.

The ConcatArrays function no longer ADAPTS STD :: Initializer_list literals due to the syntactic limitations of mutable arguments, which results in STD :: Initializer_list literals no longer being used as arguments directly:

constexpr auto x = ConcatArrays(a, {4, 5, 6}); // Error compiling

However, there is a way to get around this problem: simply convert STD :: Initializer_List to STD ::array using the tools described earlier:

constexpr auto x = ConcatArrays(a, to_array({4, 5, 6})); // OK

Compile time concatenates strings

Is STD ::array suitable for representing strings? Before answering this question, let’s see if native arrays are appropriate for representing strings:

char str[] = “abc”; // STR array size is 4, including ending ‘0’

This is a very common way to write it. Since array names can be degraded to Pointers, STR can be used in a variety of situations where strings are needed, such as to cOUT printouts. STD :: Array, as an alternative to native arrays, is also a natural fit for representing strings. Some might find it inconvenient to use STD ::array directly as a string type. But in fact STD ::array returns a pointer that can be used as a string simply by calling the data method:

constexpr auto str = to_array(“abc”); // to_array can convert a string to STD ::array static_assert(str.size() == 4); static_assert(Equals(str, “abc”)); // Equals can also accept the string literal cout << str.data(); // Prints the contents of the string

Since string literals are of type char[], all of the utility functions written earlier can take strings as input arguments. Equals above is just one example. Can ConcatArrays be used to concatenate strings? Yes, but not the way we expected:

constexpr auto str = ConcatArrays(“abc”, “def”); static_assert(str.size() == 8); // The length is not 7? static_assert(Equals(str, {‘a’, ‘b’, ‘c’, ‘0’, ‘d’, ‘e’, ‘f’, ‘0’}));

Because each string ends with the **’0′ terminator, the middle ‘0’ is not removed when they are pieced together using array concatenation, causing the resulting string to be sliced into multiple C strings. This problem can be easily solved by simply concatenating all arrays with the last element (‘0’) and adding a ‘0’ to the end of the returned array. The following code implements string concatenation. The untyped argument E is the terminator of the string, usually ‘0’**, but customization is possible. We can even use it to concatenate objects with terminators of other values, such as messages, messages, etc.

// The last character, put the terminator template constexpr auto PickChar(size_t I) {return E; }

template<auto E, typename P, typename… T> constexpr auto PickChar(size_t i, const P& p, const T&… Arr) {if (I < (size(p) -1)) {if (p[I] == E) {throw “terminator in the middle”; } return p[i]; } if (p[size(p) – 1] ! = E) {// The terminator must be the last character thrown “terminator not at end”; } return PickChar(i – (size(p) – 1), arr…) ; }

template<typename… T, auto E = ‘0’> constexpr auto ConcatStrings(const T&… str) { return GenerateArray<TotalLength(str…) – sizeof… (T) + 1>([&str…] (size_t i) { return PickChar(i, str…) ; }); } int main() { constexpr char a[] = “I “; // ConstExpr auto b = to_array(“love “); Constexpr auto STR = ConcatStrings(a, b, “C++”); constexpr auto STR = ConcatStrings(a, b, “C++”); STD ::array + string literal static_assert(Equals(STR, “I love C++”)); return 0; }

This code uses two throws to verify that the input parameters are both valid strings: string length = container length -1. If this condition is not met, the length calculation of the splicing result will be wrong.

When a compile-time evaluation throws an exception, only a compilation error occurs, so as long as ConcatStrings are not called at run time, the two throw statements don’t matter much. But because of this checksum, it’s strongly not recommended to call ConcatStrings at runtime for concatenation, and there’s no need to do it at runtime — doesn’t STD ::string addition work? One wonders: can we calculate the actual length of the string at compile time instead of using the container length? While this approach seems plausible, it’s really easy to define a function that computes the length of a string at compile time:

template<typename T, auto E = ‘0’> constexpr size_t StrLen(const T& str) noexcept { size_t i = 0; while (str[i] ! = E) { ++i; } return i; }

constexpr const char* g_str = “abc”; Int main() {// Convert a string to STD ::array constexpr auto STR = SubArray<StrLen(g_str) + 1>(g_str, 0); static_assert(Equals(str, “abc”)); return 0; }

However, once you try to put StrLen inside ConcatStrings to declare the length of the array, you get a problem: the constexpr mechanism of C++ requires that the return result of StrLen be determined to be constexpr only if the constexpr attribute of the input parameter is visible. Inside the function, the argument type you see is not constexpr. Of course we can work around and make some interesting tools, such as using evil macros:

STD ::array #define StrToArray(x) SubArray<StrLen(x) + 1>(x, 0) constexpr const char* g_str = “ABC “; Constexpr auto STR = ConcatStrings(StrToArray(g_str), “def”); int main() {constexpr auto STR = ConcatStrings(StrToArray(g_str), “def”); static_assert(Equals(str, “abcdef”)); return 0; }

With macros, ConcatStrings can be used as input indirectly even to compile-time pointer types of uncertain size [3]. You can even define a more general concatenation function that computes the length of the resulting array to the actual length of the string, if you’re really serious about using variable macros. But I seriously doubt the need for this requirement — after all, we’re just doing compile-time concatenation, and compile-time strings shouldn’t have terminators that aren’t at the end. People who see it here should at least admire the power of STD :: Array. Is it hard to do all this compile-time stuff with native arrays?

Looking ahead to C++20 — breaking more chains

How many times did I say “at least until C++20” in the article? No, but I do know that C++20 will bring a lot of nice things: STD ::array will have constexpr comparison operators; Functions can be restricted to be called only at compile time with **consteval; Template non-type parameters allow more types; STL container objects can be used as constExpr constants… All of these are minor updates to C++20, and they don’t even get mentioned in most feature introductions! You can imagine how much programming will change with C++20. We’ll look for more interesting uses then

endnotes

[1] To_array defines two versions with an lvalue reference and an rvalue reference as parameter types. Following C++11 best practices, such a function would have defined only one version and used perfect forwarding. But the to_array scenario presents a problem with universal references: C++ disallows the STD ::initializer_list literal {… } is derived as a template type parameter, and a perfect forward scenario would result in the STD :: Initializer_list literal not being used as an input parameter to to_array. We’ll see the effect of this restriction several times later.

[2]C++20 added the consteval modifier, which specifies that functions should only be called at compile time.

[3] It is important to note that when a constExpr is used to modify a pointer, it means that the pointer itself is a constant (not the object to which it points). Unlike const, constEXPr is not allowed in the middle of a type declaration expression. Therefore, to calculate the length of a string pointed to by a constexpr pointer at compile time, the string must be in the static data area, not on the stack or heap (otherwise its address cannot be determined at compile time).

STD ::array in C++ : STD ::array in C++ : STD ::array in C++

Click to follow, the first time to learn about Huawei cloud fresh technology ~