Improvement on space-time performance of C ++ 11

Source: Internet
Author: User

Improvement on space-time performance of C ++ 11
Content directory: fixed-size container array forward to list forward_list hash table [unordered container] unordered containers constant expression constexpr static asserted static_assertmove semantics and right value reference in-situ placement operation Emplace operations Reference C ++ 11 performance in time and space improvement in terms of time and space in C ++ 11; it mainly includes the following: Newly Added efficient containers: array, forward_list, and unordered containers; constant expressions, static assertions, and move semantics; fixed-size container array std :: array is a container that supports random access and has a fixed size. It is a new container in c ++ 11. It has the following features: no extra space is reserved, and only necessary space is allocated ()). You can use initializer list to initialize a table. The size information is saved. Implicit pointer type conversion is not supported. It can be considered as a good built-in array type. Example: array <int, 6> a = {1, 2, 3}; a [3] = 4; int x = a [5]; // The default data element of array is 0, so the value of x is 0 int * p1 = a; // error: std :: array cannot be implicitly converted to int * p2 =. data (); // correct. The pointer to the first element obtained by data () can be considered as a compact vector, which is more efficient than the vector (without automatic space allocation ), however, the push_back feature is missing, so that the application scenario is generally used to replace the built-in array type of c ++, rather than the vector; the previously added container to the list forward_list c ++ 11: forward list forward_list the forward list is a container that can be quickly inserted and deleted at any location (the list has this feature, and the forward list also has this feature ), however, quick random access is not supported. It is implemented using a one-way linked list. Compared with its C implementation, there is no additional overhead. Compared with std: list, this container consumes less space because it is unidirectional and not bidirectional. Std: forward_list <int> mylist (3,5); // 3 ints with value 5for (int & x: mylist) std: cout <"" <x; the hash table [unordered container] unordered containers hash container is included in many earlier compilers. For example, in earlier gcc versions, it exists in the tr1 namespace. Take unordered_map as an example, unordered_map is implemented based on the hash and unordered storage of elements; while map is implemented based on the red and black trees and ordered between elements (compared by operator <); the search time complexity of the hash version is O (1). When the data volume is large, it is much more efficient than the version of the red/black tree. It is different from the previous version in C ++ 11: // In c ++ 0x: # include <tr1/unordered_map> std: tr1: unordered_map <char, int> map1; map1.insert (std: pair <char, int> ('A', 100); // in C ++ 11: # include <unordered_map> std: unordered_map <char, int> map1; map1.insert (std :: pair <char, int> ('A', 100); constant expression constexpr Compile-time evaluation: the constant expression is in the program, some computations are irrelevant to the runtime; each execution is the same result; constant expressions allow these computations to occur at compilation rather than at runtime; in this way, the computing capability during compilation is utilized, will significantly improve the effect of program execution;

Eg: declare the function as constexprconstexpr int multiply (int x, int y) {return x * y ;}

 

// Const int val = multiply (10, 10); cin> x will be calculated during compilation; // Since the input parameter x is only determined at runtime, therefore, the following statements are not computed during compilation, but the execution is normal. const int val2 = mutliply (x, x); static assertions static_assert provide an asserted check during compilation. If the assertions are true, nothing will happen. If the assertion is false, the compiler prints a special error message. Because it is executed during compilation, it does not affect the runtime performance; expression is evaluated during compilation, when the result is false (that is, assertion fails, output the string as an error message. For example: static_assert (sizeof (long)> = 8, "64-bit code generation required for this library. "); struct S {X m1; Y m2 ;}; static_assert (sizeof (S) = sizeof (X) + sizeof (Y)," unexpecpadted ding in S "); static_assert is very useful in determining the code compilation environment, for example, determining whether the current compilation environment is 64-bit. However, since static_assert is evaluated during the compilation period, it cannot check the values that depend on the runtime calculation. For example:
Int f (int * p, int n) {// error: the expression "p = 0" is not a constant expression static_assert (p = 0, "p is not null");} the correct method is to make a judgment at runtime. If the condition is not true, an exception is thrown. The following code originally intended to be used only for integer type. Template <typename T1, typename T2> auto add (T1 t1, T2 t2)-> decltype (t1 + t2) {return t1 + t2;} but if someone writes the following code, the compiler does not report std: cout <add (1, 3.14) <std: endl; std: cout <add ("one", 2) <std: endl; the program prints the 4.14 and "e" values ". However, if we add a compile-time assertion, the above two lines will generate a compilation error. Template <typename T1, typename T2> auto add (T1 t1, T2 t2)-> decltype (t1 + t2) {static_assert (std: is_integral <T1 >:: value, "Type T1 must be integral"); static_assert (std: is_integral <T2 >:: value, "Type T2 must be integral"); return t1 + t2;} error C2338: type T2 must be integralsee reference to function template instantiation 't2 add <int, double> (T1, T2) 'being compiled with [T2 = double, T1 = int] error C2338: type T1 must be integralsee reference to function template instantiation 'T1 add <const char *, int> (T1, T2) 'being compiled with [T1 = const char *, T2 = int]

 

Move semantics and right value reference move semantics and right value Introduction The left value is an object with a name, while the right value is an unknown object (temporary object ). The move semantics allows you to modify the right value (previously, the right value is considered unchangeable and is equivalent to the const T & type ). Void incr (int & a) {++ a;} int I = 0; incr (I); // I changes to 1 // error: 0 is not a left value incr (0); // 0 is not a left value and cannot be directly bound to a non-const reference: int &. // If feasible, a temporary variable with a value of 0 will be generated during the call. // This variable is used to bind to int, however, this temporary variable will be destroyed when the function returns, // so any changes to it will be meaningless. // Therefore, the compiler rejects binding the temporary variable to a non-const reference, however, for const references, // is feasible. & indicates "right value reference ". Right Value reference can be bound to the right value (but cannot be bound to the left value): X a; X f (); X & r1 =; // bind r1 to a (a left value) X & r2 = f (); // error: f () returns the right value, cannot bind X & rr1 = f (); // OK: bind rr1 to the temporary variable X & rr2 = a; // error: you cannot bind the right-value reference rr2 to the left-value a using the following functions: template <class T> swap (T & a, T & B) // old-fashioned swap function {T tmp (a); // now there are two copies of "a" a = B; // now there are two copies of "B" B = tmp; // There are two copies of tmp (with the same value as a)} If T is a copy type with a high cost, such as string and vector, then the above swap () the Operation will also be difficult. Our original intention is not to copy these variables. I just want to copy the variables a, B, the tmp value is moved (that is, the value of a and B is exchanged through tmp). The idea behind the move assignment operation is that "assignment" does not have to be done by "copying". It can also be achieved by simply "Stealing" the source object to the target object. For example, for expression s1 = s2, we can directly "occupy" the internal data storage of s2 without copying it from s2. We can move () operator to implement "moving" of the source object ":
Template <class T> void swap (T & a, T & B) // "Perfect swap" (in most cases) {T tmp = move (); // variable a is invalid now (: Internal data is moved to tmp) a = move (B); // variable B is invalid now: internal data is moved to a, and variable a is now "Full blood") B = move (tmp); // The variable tmp is invalid now: internal data is moved to B, and variable B is now "Full blood )}

 

Move (x) means "you can use x as a right value". It may be better to change move () to rval (), but now, move () has been in use for many years. In C ++ 11, the move () template function and the reference to the right value are officially introduced. The copy operation is improved to a move operation to reduce unnecessary object creation, saving the space allocation consumption of the object and calling the structure. The move algorithm is improved based on the move std :: sort () and std: set: insert () are more than 15 times faster than the corresponding copy-based version. However, it does not improve the performance of existing operations in the standard library much because they have been optimized using similar methods (such as string, vector uses swap operations that have been optimized to replace copy ). Of course, if your code contains the move operation, you will be able to automatically benefit from the new standard library. Move improvements to containers in the C ++ 11 standard library, all containers provide mobile constructor and mobile assignment operators, those that insert new elements, such as insert () and push_back (), both have versions that can be referenced by the right value. The final result is that the performance of standard containers and algorithms has been improved without user intervention, all thanks to the reduction of copy operations. Taking vector as an example, we define "move constructors" and "move assignments" to "move" rather than copying their parameters: template <class T> class vector {//... Vector (const vector &); // copy the constructor vector (vector &); // move the constructor vector & operator = (const vector &); // copy the value assignment function vector & operator = (vector &); // move the value assignment function}; // note: the moving constructor and the moving value assignment operator accept the/non-const right value reference parameter, in addition, after adding the move constructor and the value assignment function to the container, its most important connotation is to allow us to efficiently return a container from the function:
Vector <int> make_random (int n) {vector <int> ref (n); // generates a random number between 0 and for (auto x &: ref) x = rand_int (0,255); return ref;} vector <int> v = make_random (10000); for (auto x: make_random (1000000 )) cout <x <'\ n ';

 

The key point of the above Code is that the vector is not copied (the memory space of the vector ref will not be automatically reclaimed by the stack when the function returns, move assignment uses the right value reference to precisely solve this problem ). Comparing our two common usage: allocating vector space in the free storage area, we have to handle the memory management problem; passing the parameter to the vector with allocated space, we have to write less elegant code. In most cases, push_back () uses a mobile Constructor (instead of a copy constructor) to ensure it is more efficient, but in extreme cases we can go further. Why do I have to perform copy/Move operations? Why can't we allocate space in a vector and then construct the desired object directly in this space? The operation to do this is called "place in place ). Here is an example of emplace_back:
vector<pair<string,int>> vp;string s;int i;while(cin>>s>>i) vp.emplace_back(s,i);

 

Emplace_back () accepts Variable Parameter template variables and uses them to construct the required types. Whether emplace_back () is more efficient than push_back () depends on the specific implementation of it and the variable parameter template. If you think this is an important issue, test it. Otherwise, choose them in terms of aesthetics.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.