In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-14 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article introduces the knowledge of "what is the magical use of std::array in C++ language". Many people will encounter this dilemma in the operation of practical cases, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
Td::array is a STL container added to the Cellular arrays 11 standard and is designed to provide functionality and performance similar to native arrays. Therefore, std::array has many special features that are different from other containers, such as: the elements of std::array are stored directly inside the instance, rather than allocating space on the heap; the size of std::array must be determined at compile time; the constructors, destructors and assignment operators of std::array are implicitly declared by the compiler. This makes many programmers who are used to containers like std::vector unaccustomed to it and find std::array not easy to use.
But in fact, the power of std::array may well be underestimated. In this article, I will introduce the use of std::array from various angles, hoping to bring some inspiration.
The code of this article is compiled and run under the environment of Category 17. The current mainstream version of gcc + already supports the Category 17 standard, but many versions (such as gcc 7.3) do not turn on the Clippers 17 feature by default, so you need to manually add the compilation option-std=c++17.
Automatic derivation of array size
Many projects have global arrays like this as configuration parameters:
Uint32_t g_cfgPara [] = {1,2,5,6,7,9,3,4}
When programmers want to replace native arrays with std::array, the trouble comes:
Array g_cfgPara = {1, 2, 5, 6, 7, 9, 3, 4}; / / Note template parameter "8"
The programmer has to write out the size of the array by hand because it is one of the template parameters of std::array. If the array is very long, or if you add and delete members frequently, the maintenance of the size of the array may not be so pleasant. Someone will complain: std::array 's declaration is not as easy to use as native arrays, so why choose it?
However, this complaint should be limited to prior to Category 17, which introduced the derivation of class template parameters so that you no longer need to specify class template parameters manually:
Array g_cfgPara = {1, 2, 5, 6, 7, 9, 3, 4}; / / automatic derivation of array size and member type
It looks nice, but someone will soon find something wrong: what is the type of array element? Is it still std::uint32_t?
Someone started trying to provide only element type parameters and let the compiler derive the length automatically, but unfortunately it didn't work.
Array g_cfgPara = {1,2,5,6,7,9,3,4}; / / compilation error
Well, for the time being, it seems that std::array cannot be declared like a native array. Let's solve this problem.
Return std::array with a function
The solution to the problem is to replace the class template with the function template-- because C++ allows part of the parameters of the function template to be derived automatically-- we can think of auxiliary functions such as std::make_pair and std::make_tuple. Coincidentally, the C++ standard did introduce std::make_array in the experimental version of TS v2, but this tool function was later deleted because of the derivation of class template parameters.
But obviously, the needs of users still exist. So a new auxiliary function, std::to_array, is added in Cobalt 20.
Don't be intimidated by Category 20, the code of this function is actually very simple, we can define it in our own code [1].
Templateconstexpr array to_array_impl (P & a) [N], std::index_sequence) noexcept {return {{a [I]...}};} templateconstexpr auto to_array (T (& a) [N]) noexcept {return to_array_impl (a, std::make_index_sequence {});} templateconstexpr array to_array_impl (P & & a) [N], std::index_sequence) noexcept {return {{move (a [I]).}} } templateconstexpr auto to_array (T & a) [N]) noexcept {return to_array_impl (move (a), std::make_index_sequence {});}
Careful friends will notice that the above definition differs from the recommended implementation of Category 20, which is purposeful. I'll explain why I did it later.
Now let's try a new way to solve the old problem:
Auto g_cfgPara = to_array ({1,2,5,6,7,9,3,4}); / / Type is not uint32_t?
No, why is the element type not the original std::uint32_t?
This is because the template parameter derivation rejects implicit conversion of the elements of std::initializer_list. If you change the template parameter of to_array from int to uint32_t, you will get the following compilation error:
D:\ Work\ Source_Codes\ MyProgram\ VSCode\ main.cpp:51:61: error: no matching function for call to 'to_array ()' auto g_cfgPara = to_array ({1,2,5,6,7,9,3,4}) D:\ Work\ Source_Codes\ MyProgram\ VSCode\ main.cpp:34:16: note: candidate: 'template constexpr auto to_array (T (&) [N])' constexpr auto to_array (T (& a) [N]) noexcept ^ ~ D:\ Work\ Source_Codes\ MyProgram\ VSCode\ main.cpp:34:16: note:\ Work\ Source_Codes\ MyProgram\ VSCode\ main.cpp:51:61: note: Mismatched types' unsigned int' and 'int' auto g_cfgPara = to_array ({1 2, 5, 6, 7, 9, 3, 4} D:\ Work\ Source_Codes\ MyProgram\ VSCode\ main.cpp:46:16: note: candidate: 'template constexpr auto to_array (T & &) [N])' constexpr auto to_array (T & & a) [N]) noexcept ^ ~ D:\ Work\ Source_Codes\ MyProgram\ VSCode\ main.cpp:46:16: note:\ Work\ Source_Codes\ MyProgram\ VSCode\ main.cpp:51:61: Note: mismatched types' unsigned int' and 'int'
Hoho, a little bit miserable is not, go all the way back to the origin, still can not force the specified type.
At this point, the previous changes to std::array came in handy: I added a template parameter to to_array_impl so that the elements of the input array and the elements that returned std::array were represented by different type parameters, which made it possible for the type conversion. In order to convert to the specified type, we need to add two more utility functions:
Templateconstexpr auto to_typed_array (P & a) [N]) noexcept {return to_array_impl (a, std::make_index_sequence {});} templateconstexpr auto to_typed_array (P & & a) [N]) noexcept {return to_array_impl (move (a), std::make_index_sequence {});}
The difference between these two functions and to_array is that it takes three template parameters: the first is the element type of the std::array to be returned, and the last two are the same as to_array. This allows us to customize the std::array element type by specifying the first parameter.
Auto g_cfgPara = to_typed_array ({1, 2, 5, 6, 7, 9, 3, 4}); / / automatically converts elements to uint32_t
This code can be compiled and run, but there are compilation alerts for type conversion. Of course, if you are bold enough, you can put a static_cast in the to_array_impl function to eliminate the alarm. But the compilation alarm reminds us of a problem that can't be ignored: what if the input value overflows?
Auto Groua = to_typed_array ({256,-1}); / / the number is out of the uint8_t range
The compiler will still allow you to compile, pass and run, with values of 0 and 255 for the two elements in gaccouna, respectively. If you don't understand why these two values are different from the input parameter, you should review the knowledge of integer overflow and wrapping.
Obviously, the plan is not perfect. But we can continue to improve.
Verification of the validity of literal values at compilation time
The first thing you can think of is to put a statement such as if judgment in the to_array_impl function to throw an exception or do other handling for 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 this commonly used base function. Once misjudgment is added to it, it means that the performance of each call at run time degrades.
The ideal design is that only arrays generated at compile time are checked and compilation errors are reported. However, do not add any checks when calling the function at run time.
Unfortunately, there is no way to specify that the function is only allowed to execute [2] at compile time, at least until Category 20. Is there any other way?
People familiar with C++ know that most of the compile-time processing of C++ can be done with the trick of the template-because the template parameters must be constant at compile time. So we can use template parameters to complete compile-time processing-- as long as the array elements are all untyped parameters of the template. Of course, there is a question: how to determine the type of untyped parameters of the template? It just so happens that Clipper 17 provides the function of auto template parameters, which can be used:
Templateconstexpr void CheckIntRanges () noexcept {} / / used to terminate recursive templateconstexpr void CheckIntRanges () noexcept {/ / prevent unsigned and signed comparisons static_assert (! (std::numeric_limits::min () > = 0) & & (M)
< 0))); // 范围校验 static_assert((M >= std::numeric_limits::min () & (mi;-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 before {4,2,3,1}; constexpr std::array 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;}
Because the whole sorting algorithm is completed in the compilation time, it is not necessary for us to pay too much attention to the efficiency of bubble sorting. Of course, you can write a compile-time quick sort if you want-after all, the constexpr function can also be used at run time, and it's not clear if any folly will call it at run time.
When writing constexpr functions, there are two points to note:
1. Non-constexpr functions cannot be called in constexpr functions. Therefore, you cannot use std::swap when exchanging elements, and sorting cannot call std::sort directly.
two。 The array passed in is constexpr, so the parameter type must be const, the data cannot be sorted in place, and a new array must be returned.
Although there are many limitations, the benefits of the compile-time algorithm are also huge: if there are undefined behaviors such as array crossing bounds in the operation, the compilation will fail. Compared with the run-time test, the compile-time test constexpr function can effectively intercept the problem in advance. And as long as the compilation passes, it means that the test passes, which is much more convenient than running additional white-box test cases.
The long list of static_assert statements above makes people uncomfortable. The reason for writing this is that the operator== function of std::array is not constexpr (at least until Clippers 20). But we can also define a template function to determine whether two arrays are equal:
Templateconstexpr 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;} templateconstexpr bool Equals (const T & lhs, const U & rhs) {return EqualsImpl (lhs, rhs) } templateconstexpr bool Equals (const T & lhs, const U (& rhs) [N]) {return EqualsImpl (lhs, rhs);} int main () {constexpr std::array before {4,2,3,1}; constexpr std::array 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;}
The Equals we define is more powerful than std::array 's comparison operator and can even be compared between std::array and native arrays.
There are two things to note about Equals:
1. Std::size is a utility function provided by Clipper 17, which can return its size for various containers and arrays. Of course, the Equals here will only allow the input of containers that are sized at compile time, otherwise the compilation will fail.
2. Equals defines two versions, which is forced by a restriction of C++: C++ forbids the literal amount of std::initializer_list {.} to be deduced as a template parameter type, so we must provide a version to declare that the parameter type is an array, so that {1, 2, 3, 4} this expression can be passed as a parameter.
Compile-time sorting is a heuristic attempt, and we can generate other compile-time collection constants in a similar way, such as a sequence of natural numbers of specified length:
Templateconstexpr auto NaturalNumbers () noexcept {array arr {0}; / / explicit initialization cannot save for (size_t I = 0; I < N; + + I) {arr [I] = I + 1;} return arr;} int main () {constexpr auto arr = NaturalNumbers (); static_assert (Equals (arr, {1,2,3,4,5})); return 0;}
There is no problem with compiling and running this code, but it is not recommended. The reason is that in the NaturalNumbers function, a local array with all zeros is defined, and then its value is modified one by one, so that the array that does not return the specified value directly is efficient. Some people may wonder if it is possible to remove the initialization of arr, but this will lead to a compilation error-the definition of uninitialized local variables is not allowed in the constexpr function.
Some people may think that these calculations are done at compile time and have no impact on running efficiency-- but don't forget that the constexpr function can also be called at run time. A better approach can be seen in the previous implementation of the to_array function, which allows the initialization of the array to be done in one go, eliminating the step of assigning values one by one.
With this new idea, we write a general array generator that takes a function object as a parameter and calls it to generate the value of each element of the array. The following code also demonstrates how to use this generator to generate odd sequences and Fibonacci sequences at compile time.
Templateconstexpr T OddNumber (size_t I) noexcept {return I * 2 + 1;} templateconstexpr T Fibonacci (size_t I) noexcept {if (I)
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.