I l@ve RuBoard |
![]() ![]() |
6.7 Template Parameters as StrategyOur LessThan class function object of Section 4.9 is a natural candidate for transforming into a template class: template <typename elemType> class LessThan { public: LessThan( const elemType &val ) : _val( val ){} bool operator()( const elemType &val ) const { return val < _val; } void val( const elemType &newval ) { _val = newval; } elemType val() const { return _val; } private: elemType _val; }; LessThan<int> lti( 1024 ); LessThan<string> lts( "Pooh" ); A potential problem with this implementation is that it fails if the type supplied by the user does not define a less-than operator. One possible strategy is to provide a second template class with the comparison operator factored out of the class definition. Even if this second class provides the same general semantics as LessThan, however, we must provide a unique name for it because a class template cannot be overloaded based on its parameter list. Let's call this class LessThanPred because the less-than class function object is specified as the default parameter: template <typename elemType, typename Comp = less<elemType> > class LessThanPred { public: LessThanPred( const elemType &val ) : _val( val ){} bool operator()( const elemType &val ) const { return Comp( val, _val ); } void val( const elemType &newval ) { _val = newval; } elemType val() const { return _val; } private: elemType _val; }; // alternative function object comparison class StringLen { public: bool operator()( const string &s1, const string &s2 ) { return s1.size() < s2.size(); } }; LessThanPred<int> ltpi( 1024 ); LessThanPred<string, StringLen> ltps( "Pooh" ); Alternatively, we might provide a more general name for our function object to indicate that it supports any comparison operation. In this case, it would no longer make sense to provide a default function object: template <typename elemType, typename BinaryComp > class Compare; Compare applies any BinaryComp operation against two objects of the same arbitrary elemType. In Chapter 5, we design an object-oriented numeric sequence class hierarchy. Consider the following alternative design, in which we define a numeric sequence class template with the actual sequence class factored out as a parameter: template <typename num_seq> class NumericSequence { public: NumericSequence( int len = 1, int bpos = 1 ) : _ns( len, bpos ){} // this invokes the unknown numeric sequence // member functions through a naming discipline: // each num_seq parameter class must provide a // named function calc_elems(), is_elem(), and so on ... void calc_elems( int sz ) const { _ns.calc_elems( sz ); } bool is_elem( int elem ) const { return _ns.is_elem( elem ); } // ... private: num_seq _ns; }; This template design imposes a naming discipline on the classes used as parameters: Each must provide a named function corresponding to those invoked within the NumericSequence class template, such as calc_elems(), is_elem(), and so on. Although this design idiom is somewhat advanced, I thought it worthwhile to show it briefly so that you don't fall into thinking of class template type parameters as representing only element types such as those illustrated in the binary tree implementation and the vector and list container classes of the standard library. |
I l@ve RuBoard |
![]() ![]() |