Clause 20: specify a comparison function for the container associated with the pointer
Suppose you have a set of string * pointers. you can insert the names of some animals into the set:
set<string*> ssp;// ssp = “set of string ptrs”ssp.insert(new string("Anteater"));ssp.insert(new string("Wombat"));ssp.insert(new string("Lemur"));ssp.insert(new string("Penguin"));
Then you write the following code to print the set content, and you want the string to appear in alphabetical order. Just as you expect set to keep their content in order.
For (set <string * >:: const_iterator I = ssp. Begin (); // You Want To See I! = Ssp. end (); // This: "anteater" + + I) // "lemur", "Penguin", cout <* I <Endl; // "wombat"
The comment describes what you want to see, but the result is not like this. Instead, you can see four hexadecimal numbers, which are pointer values. Because set contains pointers, * I is not a string and it is a string *. If you are using the copy algorithm,
Copy (ssp. Begin (), ssp. End (), // put the string in SSP
Ostream_iterator <string> (cout, "/N"); // copy to cout (but this cannot be compiled)
You will find that the copy call cannot be compiled. ostream_iterator needs to know the type of the printed object, so when you tell it to be a string (passed as a template parameter ), the compiler detects that the object type stored in SSP (string *) does not match, and they will prompt a compilation error.
If you angrily change * I in an explicit loop to ** I, youPossibleYou can get the output you want, but it may not. Yes, the animal names will be printed, but the chance of appearing in alphabetical order is only one of 24. SSP keeps its content in order, but it holds a pointer, So it usesPointer ValueSort by string instead of the string value. For four pointer values, there may be 24
ArrangeNote: 4! = 4*3*2*1 = 24)So there are 24 possible sequence for storing pointers. Therefore, you can see that the string is sorted by letter with a probability of 24 portions of 1. [
To overcome this problem, you should recall
Set <string *> SSP;
Is short:
Set <string *, less <string *> SSP;
Well, to be completely accurate, it is
Set <string *, less <string *>, Allocator <string *> is simplified, but the Allocator has nothing to do with us in these terms, so we will ignore them.
If you want the string * pointer to be stored in the set in the order determined by the string value, you cannot use the default less <string *> function-like class. Instead, you must write your own imitation function class. Its Objects carry a string * pointer and are sorted by the string value to which it points. Like this:
struct StringPtrLess:public binary_function<const string*,const string*,bool> {bool operator()(const string *ps1, const string *ps2) const{return *ps1 < *ps2;}};
Then you can use stringptrless as the comparison type of SSP:
Typedef set <string *, stringptrless> stringptrset;
Stringptrset SSP;
Now, your loop will finally do what you want it to do (that is, the problem you previously fixed with * I instead of ** I ):
For (stringptrset: const_iterator I = ssp. Begin (); // print "anteater", I! = Ssp. End (); // "lemur" ++ I) // "Penguin" cout <** I <Endl; // "wombat"
Note that I am writing a function object. You may wonder why you must specifically create a function-like class instead of simply writing a comparison function for set. For example, you may want to try:
Bool stringptrless (const string * ps1, // it will be used for const string * PS2) // string * pointer return * PS1 sorted by string value {// <* PS2; // comparison function} set <string *, stringptrless> SSP; // assume that stringptrless is used as the comparison function of SSP.Cannot compile
The problem here is that the third parameter of each set template isType. Unfortunately, stringptrless is not a type, it is a function. This is why stringptrless is used as a set comparison function and cannot be compiled. Set does not need a function. It needs to be instantiated internally.CreateA function type.
Whenever you create a pointer-related container, you must specify the comparison type of the container. Most of the time, your comparison type only references the pointer and compares the object to which it points (just as the stringptrless method above ). In this case, you 'd better have a simulated function template for comparison. Like this:
Struct dereferenceless {template <typename ptrtype> bool operator () (ptrtype pt1, // The parameter is passed by value, ptrtype pt2) const // because we want them to {// be (or act like) pointer return * pt1 <* pt2 ;}};
This template eliminates the need to write classes like stringptrless, because we can use dereferenceless instead: Remember that this pointer cannot be void *, and you can write a special void:
Struct dereferenceless {template <typename ptrtype> bool operator () (ptrtype pt1, // The parameter is passed by value, ptrtype pt2) const // because we want them {// to be (or behavior image) pointer return * pt1 <* pt2 ;}
Bool operator () (void * P1, void * pt2) const // because we want them
{// Yes (or behavior image) pointer assert (false );}
};
Set <string *, dereferenceless> SSP;
Oh, one more thing. These terms are related to containers of pointers, but they can also be applied to containers of Objects represented as pointers, such as smart pointers and iterators. If you have a smart pointer or an iterator associated container, you must specify a comparison type for it. Fortunately, the pointer solution can also be used for objects similar to pointers. Just as dereferenceless is suitable for comparing containers associated with T *, it can also be used as a comparison type between t object iterator and smart pointer container.