Flexible Associative Containers
I designed some drop-in replacements for the associative containers in
the Standard Template Library for C++. They work just like set,
map,
multiset, and multimap - except that you
can do searches on data types other than the
key type. The STL associative containers require you to create a
temporary object when you search for an element within the container,
and these classes eliminate the need to construct that temporary.
I called them flex_set, flex_map,
flex_multiset, and flex_multimap
because they allow for more flexible searches.
Listing 1:
class Employee
{
public:
const std::string & GetName() const;
// . . . other functions
};
struct CompareEmployees : std::binary_function< const Employee *, const Employee *, bool >
{
inline bool operator () ( const Employee * l, const Employee * r ) const
{ return ( l->GetName() < r->GetName() ); }
};
typedef std::set< Employee *, CompareEmployees > EmployeeSet;
EmployeeSet employees;
Employee * FindEmployee( const std::string & name )
{
// This bogus object is just a temporary to find the real object.
// The goal is to eliminate the need for any temporary object.
Employee bogus( name );
EmployeeSet::iterator it( employees.find( &bogus ) );
return ( employees.end() == it ) ? 0 : *it;
}
By changing several member functions of std::set to be templated
functions, and by overloading the comparison functor, the temporary
objects can be eliminated. Like the std::set class, flex_set uses
the comparison functor as a little policy class to determine how the
elements are sorted. Although the std::set has no need for an
overloaded functor, the flex_set's real flexibility comes into play
when using overloaded functors. I can design into my functor the
mechanism by which an Employee record can be compared to a std::string
or to a C-style string.
Listing 2:
template < class Key, class Compare, class Alloc >
class flex_set
{
// . . . other parts of flex_set class
public:
template< class Compare_Type >
iterator find( const Compare_Type & x );
};
Listing 3:
struct CompareEmployees : std::binary_function< const Employee *, const Employee *, bool >
{
inline bool operator () ( const Employee * l, const Employee * r ) const
{ return ( l->GetName() < r->GetName() ); }
inline bool operator () ( const Employee * l, const std::string & r ) const
{ return ( l->GetName() < r ); }
inline bool operator () ( const std::string & l, const Employee * r ) const
{ return ( l < r->GetName() ); }
};
typedef flex_set< Employee *, CompareEmployees > EmployeeSet;
EmployeeSet employees;
Employee * FindEmployee( const std::string & name )
{
// No unnecessary temporary objects here!
EmployeeSet::iterator it( employees.find( name ) );
return ( employees.end() == it ) ? 0 : *it;
}
An article I published in Overload
describes the classes in more detail. It also explains the
reasons why
various implementation decisions were made when the classes were
designed.
Here are the source code files which work
with the GCC implementation of the STL.
And a demo project
for use with MinGW.
Home Page