Valid STL Clause 30

Source: Internet
Author: User
Clause 30: ensure that the target range is large enough

When the STL containers are added (through insert, push_front, push_back, etc.), they are automatically extended to accommodate new objects. This is a good job. Some programmers are paralyzed by this belief and think they don't have to worry about making room for objects in the container, because the container can take care of these. That would be nice! When programmers want to insert objects into the container but do not tell STL what they want, the problem arises. This is a common method for self-expression:

Int transmogrify (int x); // This function generates some new values from X // vector <int> values ;... // put the data into valuesvector <int> results; // apply transmogrify to transform (values. begin (), values. end (), // results for each object in values. end (), // append the returned values transmogrify); // to results //This code has a bug!

In this example, transform is told that its target range starts with results. End (), so it is the place where the transmogrify result is called on each element of values. Just like all algorithms that use the target interval, transform writes the result by assigning values to the elements of the target interval. Transform applies transmogrify to values [0] and assigns the result to * results. end (). Then it applies transmogrify to value [1] and assigns the result to * (results. End () + 1 ). This can only cause disasters, because in * results. End ()No object, * (Results. End () + 1! It is incorrect to call transform because it will assign values to non-existent objects. (Clause 50 explains how a debugging Implementation of STL detects this issue during runtime .) Programmers who make such mistakes almost always think that the results of their algorithm calls can be inserted into the target container. If that is what you want to happen, you must say it. STL is a library, not a spirit. In this example, the following method is called:

Vector <int> results; // apply transmogrify to transform (values. begin (), values. end (), // each object in values, back_inserter (results), // transmogrify At the end of results); // Insert the returned values

Internally, the iterator returned by back_inserter calls push_back, so you can use back_inserter (that is, any standard sequence container: vector, String, deque, and list) on any container that provides push_back ). If you want to insert an algorithm into the container's front-end, you can use front_inserter. Internally, front_inserter uses push_front, so front_insert only works with the container that provides the member function (that is, deque and list ):

... // Same as list <int> results; // results is currently listtransform (values. begin (), values. end (), // frontend t_inserter (results) in results ),//In reverse orderTransmogrify); // Insert the transform result

Because front_inserter uses push_front to add each object to results, the order of objects in the results will be the same as the order of objects in the values.Opposite. This is one of the reasons why front_inserter is not as common as back_inserter. Another reason is that vector does not provide push_front, so front_inserter cannot be used for vector.

If you want transform to put the output result on the results front end, but you also need to output the same sequence of objects as in values, as long as the values is iterated in the opposite order:

List <int> results; // same as transform (values. rbegin (), values. rend (), // At the frontend of Results frontend front_inserter (results), // insert transform result transmogrify); // keep the relative object order

Front_inserter allows you to force algorithms to insert their results at the container front end. back_inserter allows you to tell them to put the results at the container backend, surprisingly, inserter allows you to force algorithms to insert their results to any location in the container:

Vector <int> values; // same as above... vector <int> results; // same as above, except for now... // before calling transform, // results already has some data transform (values. begin (), values. end (), // transmogrify inserter (results, results. begin () + results. size ()/2), // insert transmogrify into the result); // the middle of results

No matter whether you use back_inserter, front_inserter, or inserter, each insert to the destination interval completes only one object. Clause 5 explains that this may be expensive for continuous memory containers (vector, string, and deque), but clause 5 suggests a solution (using the range member function) it cannot be used to insert data using algorithms. In this example, transform writes a value to the target range each time, and you cannot change the value.

When the container you want to insert is a vector or string, you can minimize this cost by following the suggestions in Clause 14 and call reserve in advance. You still have to bear the overhead of Moving Elements every time an insert occurs, but at least you avoid re-allocating the internal memory of the container:

Vector <int> values; // same as vector <int> results ;... results. reserve (results. size () + values. size (); // determine whether the results should be at least // It can also be installed // values. size () element transform (values. begin (), values. end (), // same as above, inserter (results, results. begin () + results. size ()/2), // but results transmogrify); // No reallocation operation

When using reserve to improve the efficiency of a series of inserts, you should always remember that reserve only increases the container capacity: The container size remains unchanged. Even after calling reserve, when you want the container to add new elements to a vector or string, you must also insert an iterator into the algorithm (for example, from one of the iterators returned by back_inserter, front_inserter, or inserter ).

To fully understand this, here is an incorrect method to improve the efficiency of the example at the beginning of these terms (that is, the example of attaching the result of transmogrify acting on the data in values to results ):

Vector <int> values; // same as vector <int> results ;... results. reserve (results. size () + values. size (); // same as transform (values. begin (), values. end (), // write the transmogrify result results. end (), // to uninitialized memory transmogrify );//The behavior is undefined!

In this Code, transform happily tries to assign values to the original uninitialized memory at the end of results. In general, this will cause a runtime error, because the value assignment only makes sense when two objects are operated, rather than between an object and an original bit. Even if this code happens to do what you want it to do, results does not know that transform "creates" new "objects" on its unused capacity ". Until results knows, its size remains the same after transform is called. Similarly, its end iterator still points to the same position as before calling transform. What is the conclusion? Using reserve without inserting an iterator will lead to undefined behavior within the algorithm, and will also disrupt the container.

The correct way to write the code for this example is to use reserve and insert the iterator:

Vector <int> values; // same as the preceding vector <int> results; results. reserve (results. size () + values. size (); // same as transform (values. begin (), values. end (), // put the transmogrify result back_inserter (results), // write it to the end of results, transmogrify); // avoid reallocation During Processing

So far, I have assumed that you have asked algorithms like transform to insert their results as new elements into the container. This is the usual expectation, but sometimes you need to overwrite existing container elements rather than Insert new ones. In this case, you do not need to insert an iterator, but you still need to follow the suggestions in these terms to ensure that your destination range is large enough.

For example, assume that you want transform to overwrite the results element. If results has at least as many elements as values, it is very simple. If not, you must also use resize to ensure that it does.

Vector <int> values; vector <int> results ;... if (results. size () <values. size () {// make sure that the results must be at least results. resize (values. size (); // as large as values} transform (values. begin (), values. end (), // overwrite values. size () results. begin (), // transmogrify of results );

Alternatively, you can clear results and insert the iterator in the usual way:

... Results. clear (); // destroy all results elements in results. reserve (values. size (); // reserve sufficient space for Transform (values. begin (), values. end (), // put the transform returned value pack_inserter (results), // put it into results transmogrify );

These terms demonstrate many changes to this topic, but I hope you will remember the essence. Whenever you use an algorithm that requires a specified destination interval, make sure that the destination interval is large enough or you can increase the size when the algorithm is executed. If you choose to increase the size, use the insert iterator, such as ostream_iterators or the iterator returned from back_inserter, front_inserter, or inserter. This is all you need to remember.

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.