Valid STL Clause 39

Source: Internet
Author: User
Tags what parameter
Clause 39: use pure functions as the limit type

I hate doing this for you, but we must start with a short vocabulary lesson:

  • Predicate)Return bool (or something that can be implicitly converted to bool ). It is widely used in STL. The comparison function of the standard associated container is a sort function, which is often passed to algorithms as parameters, such as find_if and multiple sorting algorithms. (The overview of sorting algorithms can be found in Clause 31 .)
  • Pure FunctionYes. The return value only depends on the function of the parameter. If F is a pure function, X and Y are objects, the return values of f (x, y) are changed only when the value of X or Y is changed.

    In C ++, all data referenced by pure functions is constants during function lifetime, either as a parameter or not. (Generally, such constants should be declared as Const .) If the data referenced by a pure function may change in different calls, calling the function with the same parameter at different times may lead to different results, this is the opposite of pure function definition.

Now we know exactly what it means to use pure functions as the limit type. All I have to do is make you believe that my suggestions are justified. I hope you will forgive me for increasing the burden on you.

  • OneGeneric TypeIt is a function-like class, and its operator () function is an operator type, that is, its operator () returns true or false. As you can expect, any place where STL wants a declarative type, it will accept a real declarative type or a declarative class object.

That's all. I promise! Now we are ready to learn why this clause provides valuable guidance.

Clause 38 explains that function objects are passed values, so you should design function objects that can be copied. It is used for functional objects of the implicit type. There is another reason to design them to behave well when they are copied. The algorithm may copy the imitation functions, save them temporarily before use, and some algorithms use this freedom. An important result of this argument isImplicit functions must be pure functions..

To understand why, let's assume that you want to violate this constraint first. Consider the following (bad implementation) Implicit class. No matter what parameter is passed, it strictly returns true only once: the third time it is called. Otherwise, it returns false.

Class badpredicate: // For more information about this base class, public unary_function <widget, bool> {// see section 40 public: badpredicate (): timescalled (0) {} // initialize timescalled to 0 bool operator () (const widget &) {return ++ timescalled = 3;} PRIVATE: size_t timescalled ;};

Suppose we use this class to remove the third widget from a vector <widget>:

Vector <widget> VW; // create a vector, and then // put some widgets into VW. erase (remove_if (VW. begin (), // remove the third widget; VW. end (), // about the relationship between erase and remove_if badpredicate (), // see section 32 VW. end ());

This code looks reasonable, but for many STL implementations, it not only removes the third element from VW, but also removes the sixth one!

To know how this happens, let's see how remove_if is implemented. Remember remove_ifNot alwaysThis implementation:

template <typename FwdIterator, typename Predicate>FwdIterator remove_if(FwdIterator begin, FwdIterator end, Predicate p){ begin = find_if(begin, end, p); if (begin == end) return begin; else {  FwdIterator next = begin;  return remove_copy_if(++next, end. begin, p); }}

The details of this Code are not important, but note that the Attention P is first passed to find_if, and then to remove_copy_if. Of course, in both cases, p is the value-YesCopy-- To those algorithms. (Technically, this does not need to be true, but in fact,True. For more information, see Clause 38 .)

Call remove_if (the call to remove the third element from VW in user code) to create an anonymous badpredicate object, which clears the internal timescalled members. This object (called P in remove_if) is then copied to find_if, so find_if also receives a badpredicate object whose timescalled is equal to 0. Find_if "Calls" the object until it returns true, so it is called three times. find_if then returns control to remove_if. Remove_if continues to run the subsequent call remove_copy_if, and transfers another copy of P as an example. However, the timescalled member of P is still 0! Find_if does not call P, it only calls the copy of P. As a result, the remove_copy_if method is called for the third time, and true is returned. This is why remove_if will eventually delete two widgets from VW instead of one.

The simplest way to break yourself into the language trap is to declare your operator () function as const in the operator class. If you do, your compiler won't let you change any class data members.

Class badpredicate: Public unary_function <widget, bool> {public: bool operator () (const widget &) const {return ++ timescalled = 3; // error! In the const member function} // local data cannot be changed };

Because this is a straightforward method to avoid the problems we have just tested. I can almost change the question of this clause to "Making operator () a const in the operator class ". But that is not far enough. Even the const member functions can access multable data members, non-const local static objects, non-const static objects, non-const objects in the namespace domain, and non-const global objects. A well-designed generic class also ensures that its operator () function is independent of any class object. Declare operator () as const in the operator class for correct behaviorYes,Inadequate. A good operator () is of course const, but not so. It must also be a pure function.

Before these terms, I have stressed that any STL that wants a declarative type will accept a real declarative type or a declarative class object. It is correct in both directions. In any place where STL can accept a struct class object, a struct function (which may be changed by ptr_fun-see article 41) is also popular. You now understand that the operator () function in the operator class should be a pure function, so this restriction is also extended to the operator function. As an aggregate, this function is as bad as the objects generated from the badpredicate class:

Bool anotherbadpredicate (const widget &, const widget &) {static int timescalled = 0; // No! No! No! No! No! No! No! Return ++ timescalled = 3; // the quiet type should be pure function,} // pure function is stateless

No matter how you write your functions, they should all be pure functions.

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.