Reading notes effective C + + Item 44 pulling out code independent of template parameters

Source: Internet
Author: User
Tags repetition

1. Using templates may cause code bloat

Using templates is a great way to save time and avoid code reuse. You do not need to enter 20 identical class names manually, each class has 15 member functions, instead, you just need to enter a class template and have the compiler instantiate 20 specific classes and 300 functions you need. (The member functions of a class template are implicitly instantiated only if they are used, so 300 member functions are generated only if 300 functions are actually used.) ) function templates are equally appealing. You don't have to implement many functions manually, you just need to implement a function template, and then let the compiler do the rest.

However, in some cases, if you're not careful, using a template can cause code bloat: binary files that produce duplicate code or data, or both. The result may be that the source code looks neat and tidy, but the object codes are bloated and slack. Bloated slack is bad, so you need to know if you avoid such a binary exaggeration.

2. Commonality and variability analysis

Your main tool has a very impressive name: commonality and variability analysis (commonality and variability analyses), but this concept is common. Even if you never implement a template in your programming career, you will always do this analysis.

2.1 Repeating analysis of code in functions and classes

When you are implementing a function, you realize that some parts of the implementation of the function are basically the same as another function implementation, will you repeat the code? Of course not. You extract the public code for two functions, put it into the third function, and then call the new function in two functions. To summarize, you analyze two functions, find the same and different parts, move the same parts into a new function, and leave the different parts in the original function. Similarly, if you are implementing a class and you realize that part of another class in the class is the same, you should not rewrite the same part. Instead, you can move the same parts into a new class and then use inheritance or composition (Item 32,item 38,item 39) to have the original class access the common attributes. The different parts of the original class remain in their original positions.

2.2 Code repetition analysis and elimination repetition method in template

When you implement a template, you do the same analysis, and you use the same way to prevent repetition, but there is a place where you can hurt yourself. In non-template (non-template) code, repetition is displayed: You can see code duplication between functions or between classes. In the template code, repetition is implicit: there is only one copy of the template source, so you have to train yourself when a template is instantiated multiple times, you can feel the repetition will not happen .

2.2.1 Eliminate code bloat first off--remove non-type parameters

For example, suppose you want to implement a template for a fixed-size matrix, and you need to support transpose of the matrix.

1Template<typename T,//template for n x n matrices of2std::size_t n>//objects of type T; see below for info3 classSquarematrix {//On the size_t parameter4  Public:5 ...6 7 voidInvert ();//invert the matrix in place8 9};

This template has a type parameter, T, but also with a type size_t parameter, a non-type (non-type) parameter. Non-type parameters are less common than type parameters, but they are perfectly legal, and in this case they can be very natural.

Now consider the following code:

1squarematrix<Double,5>SM1;2 3 ...4 5 Sm1.invert ();6 7 //Call squarematrix<double, 5>::invert8 9squarematrix<Double,Ten>sm2;Ten  One   A  - ... -  the   -  - Sm2.invert (); -  + //Call squarematrix<double, 10>::invert -  +  

Two copies of the invert will be instantiated here. The two functions are not the same because one works on the 5*5 matrix and the other works on the 10*10 matrix, but the two functions will be the same if the constants 5 and 10 are not considered. This is a typical way to inflate the code that contains the template.

If you see two functions, all of their characters are the same, except one version using 5 and the other using 10, what do you do next? Your gut instinct is to create a version of the function with one argument, and then call the function with 5 or 10 as a parameter instead of repeating the code. Your intuition can serve you well! This is the first level of implementation of Squarematrix:

1Template<typename t>//size-independent base class for2 classSquarematrixbase {//square matrices3 protected:4 ...5 voidInvert (std::size_t matrixsize);//Invert matrix of the given size6 ...7 };8Template<typename T, std::size_t n>9 classSquarematrix:PrivateSquarematrixbase<t> {Ten Private: One usingsquarematrixbase<t>::invert;//Make base class version of Invert A //visible in this class, see Items - //and Item -  Public: the ... - voidInvert () {invert (n);}//Make-inline call to base class -};//version of Invert

As you can see, the invert version with the parameter is placed in the base class Squarematrixbase. Like Squarematrix, Squarematrixbase is a template, but unlike Squarematrix, it only templates object types in the matrix. Therefore, all matrices that contain a given type of object will share a single squarematrixbase class. This allows them to share a single copy of the invert version of the Squarematrixbase class. (You cannot declare it as inline, because once it is inline, each Squarematrix::invert instance will get a copy of the Squarematrixbase::invert code (see item 30), You will find that you have returned to the original point of the object code repetition. )

Squarematrixbase::invert is used only to prevent code duplication in derived classes, so it is protected instead of public. The additional cost of calling it should be 0, because the inverts call base class version of the derived class uses the inline function. (inline is implicitly shown in item 30) and notice that the inheritance between Squarematrix and Squaremarixbase is private. This precisely reflects the fact that the only reason to use the base class is to help derive the implementation of the class, not to express the " squarematrix " between the Squarematrixbase and the is-a"relationship . (For information on private inheritance, see item 39)

2.2.2 Eliminate Code bloat second level--how derived classes tell the base class where the data is

It all looks good so far, but there's a tricky problem we don't have to deal with. How does Squarematrixbase::invert know what data to operate on? It learns the size of the rectangle from the argument, but how does it know where the data is provided for the particular matrix? Probably only derived classes will know. Derived classes such as the Ho Tongki class communicate to allow the base class to execute invert?

One possible way is to add another parameter to Squarematrixbase::invert, which may be a pointer to a piece of memory that holds the rectangular data in memory. This approach works, but in all likelihood, invert is not present in Squarematrix and can be overridden in a size-independent manner, and is moved into the only function in Squarematrixbase. If there are several such functions, we need a way to find the memory that holds the rectangle's data, we can add an extra parameter to all the functions, but we have repeatedly told Squarematrixbase the same message. This seems to be wrong.

A replacement method is to have squarematrixbase store a pointer to the memory that holds the rectangle's data. This has the same effect as storing the rectangle size. The results are as follows:

1Template<typename t>2 classSquarematrixbase {3 protected:4Squarematrixbase (std::size_t N, t *pmem)//store matrix size and a5: Size (n), PData (Pmem) {}//ptr to matrix values6 7 voidSetdataptr (T *ptr) {pData = ptr;}//Reassign PData8 9 ...                                                            Ten  One Private:                                                   A  -   -  thestd::size_t size;//Size of Matrix -  -T *pdata;//pointer to matrix values -  +  -};

This allows derived classes to decide how to allocate memory. Some implementations store the rectangle data inside the Squarematrix object:

1Template<typename T, std::size_t n>2 classSquarematrix:PrivateSquarematrixbase<t> {3  Public:4Squarematrix ()//Send matrix size and5: Squarematrixbase<t> (n, data) {}//data ptr to base class6 ...7 Private:8T data[n*n];9};

There is no need for this type of object to do dynamic memory allocation, but the object itself can be very large. One alternative is to store the data on the heap for each rectangle:

1Template<typename T, std::size_t n>2 classSquarematrix:PrivateSquarematrixbase<t> {3  Public:4Squarematrix ()//set base class data ptr to NULL,5: Squarematrixbase<t> (N,0),//allocate memory for matrix6PData (NewT[n*n])//values, save a PTR to the7{ This->setdataptr (PData.Get()); }//memory, and give a copy of it8 9...//To the base classTen  One Private: ABoost::scoped_array<t> PData;//See Item to info on -  -   the  -};//Boost::scoped_array

2.2.3 Elimination of code expansion before and after efficiency comparison

Regardless of where the data is stored, from the point of view of code bloat, the key result is that many (perhaps all)Squarematrix member functions can simply call the base class's (Non-inline) function version, All rectangles that hold the same type of data share the functions in the base class, regardless of the size. At the same time, Squarematrix objects of different size belong to different types, so even squarematrix<double,5> and squarematrix<double,10> The object uses the same member function in squarematrixbase<double> to pass a Squarematrix<double,5> object to a required squarematrix<double,10 The function of > is not a chance. Good or bad.

Good is good, but there is a price to pay. A invert version of a rectangular size that is fixed is more likely to produce better code than a invert version that passes a size (or stored in an object) as a function parameter. For example, in a version that specifies size, sizes is a compile-time constant, so it is a qualified person for a constant propagation optimization, or it can be placed in a build instruction as a direct operand. This cannot be done in versions unrelated to size.

On the other hand, providing only one invert version of a matrix of different sizes reduces the size of the executable, which reduces the working set size of the program and strengthens the referential centralization of the instruction cache. These things can make the program run faster and optimize relative to the version specified by size, and it may make better compensation. Which method works better? The only way to do this is to try both methods to observe their behavior on your particular platform and on a representative set of data.

Another area of concern about efficiency is the size of the object. If you don't mind, moving the size-independent version up to the base class increases the size of each object. For example, in the code I just showed, each Squarematrix object has a pointer to the data in the Squarematrixbase class. Even if there is already a method for obtaining data in each derived class, this also increases the size of at least one pointer for each Squarematrix object. We can modify the design to get rid of pointers, but this is also a price to pay. For example, let the base class store a protected pointer to the data, but it can result in a reduced encapsulation (Item 22). It can also lead to resource management complications: If the base class stores pointers to matrix data, but the data is either dynamically allocated or stored in derived class objects (as we see), how do you decide if you need a delete pointer? There is an answer to this question, but the finer things you do, the more complex it becomes. In a sense, a little bit of code repetition starts to get a little lucky.

2.3 How to handle code bloat caused by type template parameters

This article only discusses the code bloat caused by non-type template parameters, but the type parameters can also cause code bloat. For example, in many platforms, int and long have the same binary representation, so using vector<int> and vector<long> in member functions will look the same, which is exactly what the code bloat defines. Some connectors incorporate the same code implementation, but some do not, which means that the int and long versions instantiated by the template cause code bloat in some environments. Similarly, on most platforms, all pointer types have the same binary representation, so a template with a pointer type (for example, list<int*>,list<const*>,list<squarematrix<long,3 >*> and so on) should generally be able to use a single underlying implementation for each member function. In particular, this means that when implementing a member function of a strongly typed pointer (t* pointer), let them invoke a function with an untyped pointer (void* pointer). The implementation of some standard C + + libraries is what templates do (such as vector,deque, and list). If you care about the code bloat problem that appears in your template, you may want to open a template that does the same thing.

3. Summary
    • Templates produce multiple classes and multiple functions, so any template should not rely on template parameters that cause code bloat.
    • Code bloat caused by non-type template parameters Typically, you can replace a template parameter with a function parameter or a class data member to purge.
    • Code bloat caused by type parameters can also be reduced by sharing the same binary representation for the instantiated type.

Reading notes effective C + + Item 44 pulling out code independent of template parameters

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.