C++ & operator []=

by Matthew Wilson



Listing One

// Alternative op [] semantics, via argument decoration

template<class T>

struct inserter

{

  inserter(T const &arg)

    : argument(arg)

  {}

  T const &argument;

};



// "maker" function

template<class T>

inline inserter<T> insert(T const &arg)

{

  return inserter<T>(arg);

}

template< typename K // Key type

        , typename R // Reference type

        , . . .

        >

class umap

{

  . . .

  // Some "standard" map methods, used in the new ones below

  std::pair<iterator, bool> insert(value_type const &value);

  iterator                  find(key_type const &key);

  const_iterator            find(key_type const &key) const;



  /// This method looks up the value for the given key

  /// \note This does <b>not</b> perform insertions

  referent_type const &operator [](key_type const &key) const

  {

    const_iterator  it  = this->find(key);

    if(this->end() == it)

    {

      throw new std::out_of_range("invalid key");

    }

    return (*it).second;

  }

  /// This method looks up the value for the given key

  /// \note This does <b>not</b> perform insertions

  referent_type   &operator [](key_type const &key)

  {

    iterator  it  = this->find(key);



    if(this->end() == it)

    {

      throw new std::out_of_range("invalid key");

    }

    return (*it).second;

  }

  /// Specialised override which performs insertions

  template<class T1>

  referent_type   &operator [](inserter<T1> const &key)

  {

    // How's this for a nice little statement ... ?? ;-)

    return (*this->insert(key.argument).first).second;

  }

  . . .

};



Listing Two



// An unambiguous map: implemented in terms of std::map

  . . . // All the inserter / insert() stuff from Listing 1

template< typename K                     // Key type

        , typename R                     // Reference type

        , typename P = std::less<K>      // Comparison predicate

        , typename A = std::allocator<T> // Allocator

        >

class umap // unambiguous map. Not too cheeky, eh?

{

/// \name Types

/// @{

private:

  typedef std::map<K, T, P, A>                    map_impl_type;

public:

  typedef typename map_impl_type::key_type        key_type;

  typedef typename map_impl_type::referent_type   referent_type;

  typedef typename map_impl_type::key_compare     key_compare;

  typedef typename map_impl_type::allocator_type  allocator_type;

  typedef typename map_impl_type::value_type      value_type;

  typedef typename map_impl_type::value_compare   value_compare;

  typedef umap<K, T, P, A>                        class_type;

  typedef typename map_impl_type::size_type       size_type;

  typedef typename map_impl_type::difference_type difference_type;

  typedef typename map_impl_type::reference       reference;

  typedef typename map_impl_type::const_reference const_reference;

  typedef typename map_impl_type::iterator        iterator;

  typedef typename map_impl_type::const_iterator  const_iterator;

  typedef typename map_impl_type::reverse_iterator reverse_iterator;

  typedef typename map_impl_type::const_reverse_iterator  

                                             const_reverse_iterator;

/// @}

/// \name Construction

/// @{

public:

  explicit umap(key_compare const     &pred = key_compare()

              , allocator_type const  &ator = allocator_type())

    : m_map(pred, ator)

  {}

  umap(class_type const &rhs)

    : m_map(rhs.m_map)

  {}

#ifdef __STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT

  template <typename I>

  umap(I from, I to

#else /* ? __STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */

  umap( value_type const *from, value_type const *to

#endif /* __STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */

      , key_compare const &pred = key_compare()

      , allocator_type const &ator = allocator_type())

    : m_map(from, to, pred, ator)

  {}

/// @}

/// \name Member access and keyed-insertion operations

/// @{

  /// This method looks up the value for the given key

  /// \note This does <b>not</b> perform insertions

  referent_type const &operator [](key_type const &key) const

  {

    const_iterator  it  = m_map.find(key);

    if(m_map.end() == it)

    {

      throw new std::out_of_range("invalid key");

    }

    return (*it).second;

  }

  /// This method looks up the value for the given key

  /// \note This does <b>not</b> perform insertions

  referent_type   &operator [](key_type const &key)

  {

    iterator  it  = m_map.find(key);

    if(m_map.end() == it)

    {

      throw new std::out_of_range("invalid key");

    }

    return (*it).second;

  }

  /// Specialised override which performs insertions

  template<class T1>

  referent_type   &operator [](inserter<T1> const &key)

  {

    return m_map[key.argument];

  }

  referent_type       &at(key_type const &key)

  {

    return operator [](key);

  }

  referent_type const &at(key_type const &key) const

  {

    return operator [](key);

  }

  std::pair<iterator, ss_bool_t>  insert(value_type const &value)

  {

    return m_map.insert(value);

  }

  iterator                insert(iterator it, value_type const &value)

  {

    return m_map.insert(it, value);

  }

#ifdef __STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT

  template <typename I>

  void insert(I from, I to)

#else /* ? __STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */

  void insert(value_type const *from, value_type const *to)

#endif /* __STLSOFT_CF_MEMBER_TEMPLATE_CTOR_SUPPORT */

  {

    return m_map.insert(from, to)

  }

/// @}

  // All of the rest of the methods forward directly to m_map

/// \name Operations

/// @{

public:

  iterator  erase(iterator it)

  {

    return m_map.erase(it);

  }

  iterator  erase(iterator first, iterator last);

  size_type erase(key_type const &key);

  void      clear();

  void      swap(class_type const &rhs);

/// @}

/// \name Attributes

/// @{

public:

  size_type       size() const

  {

    return m_map.size();

  }

  size_type       max_size() const;

  bool            empty() const;

  allocator_type  get_allocator() const;

  key_compare     key_comp() const

  value_compare   value_comp() const;

/// @}

/// \name Searching

/// @{

public:

  iterator                                  find(key_type const &key)

  {

    return m_map.find(key);

  }

  const_iterator                 find(key_type const &key) const;

  size_type                      count(key_type const &key) const;

  iterator                       lower_bound(key_type const &key);

  const_iterator                 lower_bound(key_type const &key) const;

  iterator                       upper_bound(key_type const &key);

  const_iterator                 upper_bound(key_type const &key) const;

  std::pair<iterator, iterator>  equal_range(key_type const &key);

  std::pair<const_iterator, const_iterator> 

                              equal_range(key_type const &key) const;

/// @}

/// \name Iteration

/// @{

public:

  iterator                begin();

  iterator                end();

  const_iterator          begin() const;

  const_iterator          end() const;

  reverse_iterator        rbegin();

  reverse_iterator        rend();

  const_reverse_iterator  rbegin() const;

  const_reverse_iterator  rend() const;

  {

    return m_map.rend();

  }

/// @}

private:

  map_impl_type m_map;

};





Listing Three



// An unambiguous map: a veneer over std::map

  . . . // All the inserter / insert() stuff from Listing 1

template< typename K                     // Key type

        , typename R                     // Reference type

        , typename P = std::less<K>      // Comparison predicate

        , typename A = std::allocator<T> // Allocator

        >

class umap

  : public std::map<K, R, P, A> // Inherits publicly

{

/// \name Types

/// @{

private:

  typedef std::map<K, T, P, A>                       parent_class_type;

public:

  typedef typename parent_class_type::key_type       key_type;

  typedef typename parent_class_type::referent_type  referent_type;

  typedef typename parent_class_type::key_compare    key_compare;

  typedef typename parent_class_type::allocator_type allocator_type;

  typedef typename parent_class_type::value_type     value_type;

  typedef typename parent_class_type::value_compare  value_compare;

  typedef umap<K, T, P, A>                           class_type;

  typedef typename parent_class_type::size_type      size_type;

  typedef typename parent_class_type::difference_type difference_type;

  typedef typename parent_class_type::reference       reference;

  typedef typename parent_class_type::const_reference const_reference;

  typedef typename parent_class_type::iterator        iterator;

  typedef typename parent_class_type::const_iterator  const_iterator;

  typedef typename parent_class_type::reverse_iterator reverse_iterator;

  typedef typename parent_class_type:

             :const_reverse_iterator  const_reverse_iterator;

/// @}

/// \name Construction

/// @{

public:

  explicit umap(key_compare const     &pred = key_compare()

              , allocator_type const  &ator = allocator_type())

    : parent_class_type(pred, ator)

  {}

  umap(class_type const &rhs)

    : parent_class_type(rhs)

  {}

  template <typename I>

  umap( I from, I to

      , key_compare const &pred = key_compare()

      , allocator_type const &ator = allocator_type())

    : parent_class_type(from, to, pred, ator)

  {}

/// @}



/// \name Member access and keyed-insertion operations

/// @{

  /// This method looks up the value for the given key

  /// \note This does <b>not</b> perform insertions

  referent_type const &operator [](key_type const &key) const

  {

    const_iterator  it  = this->find(key);

    if(this->end() == it)

    {

      throw new std::out_of_range("invalid key");

    }

    return (*it).second;

  }

  /// This method looks up the value for the given key

  ///

  /// \note This does <b>not</b> perform insertions

  referent_type   &operator [](key_type const &key)

  {

    iterator  it  = this->find(key);



    if(this->end() == it)

    {

      throw new std::out_of_range("invalid key");

    }

    return (*it).second;

  }

  /// Specialised override which performs insertions

  template<class T1>

  referent_type   &operator [](inserter<T1> const &key)

  {

    // How's this for a nice little statement ... ?? ;-)

    return parent_class_type::operator [](key.argument);

  }

  referent_type       &at(key_type const &key)

  {

    return operator [](key);

  }

  referent_type const &at(key_type const &key) const

  {

    return operator [](key);

  }

  // All other methods are visible by inheritance from the veneer

  // base class.

};











7





