diff --git a/Control/AthenaKernel/AthenaKernel/CondCont.h b/Control/AthenaKernel/AthenaKernel/CondCont.h
index 358f45c8fa81f984c4e31409fbea5bd05a4e3497..81515ec7275b272120ed7e54eae181dadc108f44 100644
--- a/Control/AthenaKernel/AthenaKernel/CondCont.h
+++ b/Control/AthenaKernel/AthenaKernel/CondCont.h
@@ -114,27 +114,28 @@ public:
   /**
    * @brief Return CLID/key corresponding to this container.
    */
-  virtual const DataObjID& id() const = 0;
+  const DataObjID& id() const;
 
 
   /**
    * @brief Return the associated @c DataProxy, if any.
    */
-  virtual SG::DataProxy* proxy() = 0;
+  SG::DataProxy* proxy();
 
 
   /**
    * @brief Set the associated @c DataProxy.
    * @param proxy The proxy to set.
    */
-  virtual void setProxy(SG::DataProxy*) = 0;
+  void setProxy(SG::DataProxy*);
 
 
   /**
    * @brief Dump the container contents for debugging.
    * @param ost Stream to which to write the dump.
    */
-  virtual void list (std::ostream& ost) const = 0;
+  virtual
+  void list (std::ostream& ost) const;
 
 
   /**
@@ -146,27 +147,30 @@ public:
   /**
    * @brief Return the number of conditions objects in the container.
    */
-  virtual size_t entries() const = 0;
+  size_t entries() const;
 
   
   /**
    * @brief Return the number of run+LBN conditions objects
    *        in the container.
    */
-  virtual size_t entriesRunLBN() const = 0;
+  virtual
+  size_t entriesRunLBN() const;
 
 
   /**
    * @brief Return the number of timestamp-based conditions objects
    *        in the container.
    */
-  virtual size_t entriesTimestamp() const = 0;
+  virtual
+  size_t entriesTimestamp() const;
 
 
   /**
    * @brief Return all IOV validity ranges defined in this container.
    */
-  virtual std::vector<EventIDRange> ranges() const = 0;
+  virtual
+  std::vector<EventIDRange> ranges() const;
 
 
   /** 
@@ -185,16 +189,18 @@ public:
    * duplicates an existing one, and FAILURE otherwise
    * (ownership of the object will be taken in any case).
    */
-  virtual StatusCode typelessInsert (const EventIDRange& r,
-                                     void* obj,
-                                     const EventContext& ctx = Gaudi::Hive::currentContext()) = 0;
+  virtual
+  StatusCode typelessInsert (const EventIDRange& r,
+                             void* obj,
+                             const EventContext& ctx = Gaudi::Hive::currentContext());
 
 
   /**
    * @brief Test to see if a given IOV time is mapped in the container.
    * @param t IOV time to check.
    */
-  virtual bool valid( const EventIDBase& t) const = 0;
+  virtual
+  bool valid( const EventIDBase& t) const;
 
 
   /**
@@ -204,7 +210,8 @@ public:
    *
    * Returns true if @c t is mapped; false otherwise.
    */
-  virtual bool range (const EventIDBase& t, EventIDRange& r) const = 0;
+  virtual
+  bool range (const EventIDBase& t, EventIDRange& r) const;
 
 
   /**
@@ -212,8 +219,9 @@ public:
    * @param IOV time of element to erase.
    * @param ctx Event context for the current thread.
    */
-  virtual void erase (const EventIDBase& t,
-                      const EventContext& ctx = Gaudi::Hive::currentContext()) = 0;
+  virtual
+  void erase (const EventIDBase& t,
+              const EventContext& ctx = Gaudi::Hive::currentContext());
 
 
   /**
@@ -235,7 +243,8 @@ public:
    *
    * Returns the number of objects that were removed.
    */
-  virtual size_t trimRunLBN (const std::vector<key_type>& keys) = 0;
+  virtual
+  size_t trimRunLBN (const std::vector<key_type>& keys);
 
 
   /**
@@ -257,7 +266,8 @@ public:
    *
    * Returns the number of objects that were removed.
    */
-  virtual size_t trimTimestamp (const std::vector<key_type>& keys) = 0;
+  virtual
+  size_t trimTimestamp (const std::vector<key_type>& keys);
 
 
   /**
@@ -267,19 +277,19 @@ public:
    * This would normally be done through RCU service.
    * Defined here for purposes of testing.
    */
-  virtual void quiescent (const EventContext& ctx = Gaudi::Hive::currentContext()) = 0;
+  void quiescent (const EventContext& ctx = Gaudi::Hive::currentContext());
 
 
   /**
    * @brief Return the number times an item was inserted into the map.
    */
-  virtual size_t nInserts() const = 0;
+  size_t nInserts() const;
 
 
   /**
    * @brief Return the maximum size of the map.
    */
-  virtual size_t maxSize() const = 0;
+  size_t maxSize() const;
 
 
   /**
@@ -292,8 +302,9 @@ public:
    * IOV is changed to the end time for @c newRange.  (If the end time for @c newRange
    * is before the end of the last IOV, then nothing is changed.)
    */
-  virtual StatusCode extendLastRange (const EventIDRange& newRange,
-                                      const EventContext& ctx = Gaudi::Hive::currentContext()) = 0;
+  virtual
+  StatusCode extendLastRange (const EventIDRange& newRange,
+                              const EventContext& ctx = Gaudi::Hive::currentContext());
 
 
   /**
@@ -377,17 +388,56 @@ public:
 
   
 protected:
+  typedef CxxUtils::ConcurrentRangeMap<RangeKey, key_type, void, Compare,
+    Athena::RCUUpdater>
+    CondContSet;
+
+  typedef CondContSet::Updater_t Updater_t;
+
   /**
    * @brief Internal constructor.
    * @param rcusvc RCU service instance.
    * @param CLID of the most-derived @c CondCont.
    * @param id CLID+key for this object.
    * @param proxy @c DataProxy for this object.
+   * @param delfcn Deletion function for the actual payload type.
+   * @param capacity Initial capacity of the container.
    */
   CondContBase (Athena::IRCUSvc& rcusvc,
                 CLID clid,
                 const DataObjID& id,
-                SG::DataProxy* proxy);
+                SG::DataProxy* proxy,
+                CondContSet::delete_function* delfcn,
+                size_t capacity);
+                
+
+  /** 
+   * @brief Insert a new conditions object.
+   * @param r Range of validity of this object.
+   * @param t Pointer to the object being inserted.
+   * @param ctx Event context for the current thread.
+   *
+   * Returns SUCCESS if the object was successfully inserted;
+   * DUPLICATE if the object wasn't inserted because the range
+   * duplicates an existing one, and FAILURE otherwise
+   * (ownership of the object will be taken in any case).
+   */
+  StatusCode insertBase (const EventIDRange& r,
+                         CondContSet::payload_unique_ptr t,
+                         const EventContext& ctx = Gaudi::Hive::currentContext());
+
+
+  /** 
+   * @brief Internal lookup function.
+   * @param t IOV time to find.
+   * @param r If non-null, copy validity range of the object here.
+   *
+   * Looks up the conditions object corresponding to the IOV time @c t.
+   * If found, return the pointer (as a pointer to the payload type
+   * of the most-derived CondCont).  Otherwise, return nullptr.
+   */
+  const void* findBase (const EventIDBase& t,
+                        EventIDRange const** r) const;
 
 
   /**
@@ -402,19 +452,19 @@ protected:
   const void* cast (CLID clid, const void* ptr) const;
 
 
-  /** 
-   * @brief Internal lookup function.
+  /**
+   * @brief Do pointer conversion for the payload type.
    * @param clid CLID for the desired pointer type.
-   * @param t IOV time to find.
-   * @param r If non-null, copy validity range of the object here.
+   * @param ptr Pointer of type @c T*.
    *
-   * Looks up the conditions object corresponding to the IOV time @c t.
-   * If found, convert the pointer to a pointer to the type identified
-   * by CLID and return it.  Otherwise, return nullptr.
+   * Converts @c ptr from @c T* to a pointer to the type
+   * given by @c clid.  Returns nullptr if the conversion
+   * is not possible.
+   *
+   * This is a virtual function that calls @c cast from the most-derived class
+   * of the hierarchy.
    */
-  virtual const void* findByCLID (CLID clid,
-                                  const EventIDBase& t,
-                                  EventIDRange const** r) const = 0;
+  virtual const void* doCast (CLID clid, const void* ptr) const = 0;
 
 
   /**
@@ -427,6 +477,15 @@ private:
   /// CLID of the most-derived @c CondCont
   CLID m_clid;
 
+  /// CLID+key for this container.
+  DataObjID m_id;
+
+  /// Associated @c DataProxy.
+  SG::DataProxy* m_proxy;
+
+  /// Sets of mapped objects, by timestamp and run+LBN.
+  CondContSet m_condSet_clock, m_condSet_RE;
+
   /// Handle to the cleaner service.
   ServiceHandle<Athena::IConditionsCleanerSvc> m_cleanerSvc;
 
@@ -523,6 +582,8 @@ public:
   /// Base class.
   typedef typename CondContBaseInfo<T>::Base Base;
 
+  typedef typename Base::CondContSet CondContSet;
+
   /// Payload type held by this class.
   typedef T Payload;
 
@@ -550,79 +611,6 @@ public:
   CondCont& operator= (const CondCont&) = delete;
 
 
-  /**
-   * @brief Return CLID/key corresponding to this container.
-   */
-  virtual const DataObjID& id() const override;
-
-
-  /**
-   * @brief Return the associated @c DataProxy, if any.
-   */
-  virtual SG::DataProxy* proxy() override;
-
-
-  /**
-   * @brief Set the associated @c DataProxy.
-   * @param proxy The proxy to set.
-   */
-  virtual void setProxy (SG::DataProxy* proxy) override;
-
-
-  /**
-   * @brief Dump the container contents for debugging.
-   * @param ost Stream to which to write the dump.
-   */
-  virtual void list (std::ostream& ost) const override;
-
-
-  /**
-   * @brief Return the number of conditions objects in the container.
-   */
-  virtual size_t entries() const override;
-
-
-  /**
-   * @brief Return the number of run+LBN conditions objects
-   *        in the container.
-   */
-  virtual size_t entriesRunLBN() const override;
-
-
-  /**
-   * @brief Return the number of timestamp-based conditions objects
-   *        in the container.
-   */
-  virtual size_t entriesTimestamp() const override;
-
-
-  /**
-   * @brief Return all IOV validity ranges defined in this container.
-   */
-  virtual std::vector<EventIDRange> ranges() const override;
-
-
-  /** 
-   * @brief Insert a new conditions object.
-   * @param r Range of validity of this object.
-   * @param obj Pointer to the object being inserted.
-   * @param ctx Event context for the current thread.
-   *
-   * @c obj must point to an object of type @c T,
-   * except in the case of inheritance, where the type of @c obj must
-   * correspond to the most-derived @c CondCont type.
-   * The container will take ownership of this object.
-   *
-   * Returns SUCCESS if the object was successfully inserted;
-   * DUPLICATE if the object wasn't inserted because the range
-   * duplicates an existing one, and FAILURE otherwise
-   * (ownership of the object will be taken in any case).
-   */
-  virtual StatusCode typelessInsert (const EventIDRange& r,
-                                     void* obj,
-                                     const EventContext& ctx = Gaudi::Hive::currentContext()) override;
-
-
   /** 
    * @brief Insert a new conditions object.
    * @param r Range of validity of this object.
@@ -656,103 +644,6 @@ public:
              EventIDRange const** r = nullptr) const;
 
 
-  /**
-   * @brief Test to see if a given IOV time is mapped in the container.
-   * @param t IOV time to check.
-   */
-  virtual bool valid (const EventIDBase& t) const override;
-
-
-  /**
-   * @brief Return the mapped validity range for an IOV time.
-   * @param t IOV time to check.
-   * @param r[out] The range containing @c t.
-   *
-   * Returns true if @c t is mapped; false otherwise.
-   */
-  virtual bool range (const EventIDBase& t, EventIDRange& r) const override;
-
-
-  /**
-   * @brief Erase the first element not less than @c t.
-   * @param IOV time of element to erase.
-   * @param ctx Event context for the current thread.
-   */
-  virtual void erase (const EventIDBase& t,
-                      const EventContext& ctx = Gaudi::Hive::currentContext()) override;
-
-
-  /**
-   * @brief Remove unused run+LBN entries from the front of the list.
-   * @param keys List of keys that may still be in use.
-   *             (Must be sorted.)
-   *
-   * We examine the objects in the container, starting with the earliest one.
-   * If none of the keys in @c keys match the range for this object, then
-   * it is removed from the container.  We stop when we either find
-   * an object with a range matching a key in @c keys or when there
-   * is only one object left.
-   *
-   * The list @c keys should contain keys as computed by keyFromRunLBN.
-   * The list must be sorted.
-   *
-   * Removed objects are queued for deletion once all slots have been
-   * marked as quiescent.
-   *
-   * Returns the number of objects that were removed.
-   */
-  virtual size_t trimRunLBN (const std::vector<key_type>& keys) override;
-
-
-  /**
-   * @brief Remove unused timestamp entries from the front of the list.
-   * @param keys List of keys that may still be in use.
-   *             (Must be sorted.)
-   *
-   * We examine the objects in the container, starting with the earliest one.
-   * If none of the keys in @c keys match the range for this object, then
-   * it is removed from the container.  We stop when we either find
-   * an object with a range matching a key in @c keys or when there
-   * is only one object left.
-   *
-   * The list @c keys should contain keys as computed by keyFromRunLBN.
-   * The list must be sorted.
-   *
-   * Removed objects are queued for deletion once all slots have been
-   * marked as quiescent.
-   *
-   * Returns the number of objects that were removed.
-   */
-  virtual size_t trimTimestamp (const std::vector<key_type>& keys) override;
-
-  
-  /**
-   * @brief Mark that this thread is no longer accessing data from this container.
-   * @param ctx Event context for the current thread.
-   *
-   * This would normally be done through RCU service.
-   * Defined here for purposes of testing.
-   */
-  virtual void
-  quiescent (const EventContext& ctx /*= Gaudi::Hive::currentContext()*/) override;
-
-
-  /**
-   * @brief Return the number times an item was inserted into the map.
-   */
-  virtual size_t nInserts() const override;
-
-
-  /**
-   * @brief Return the maximum size of the map.
-   */
-  virtual size_t maxSize() const override;
-
-
-  virtual StatusCode extendLastRange (const EventIDRange& newRange,
-                                      const EventContext& ctx = Gaudi::Hive::currentContext()) override;
-
-
 protected:
   /**
    * @brief Internal constructor.
@@ -760,11 +651,15 @@ protected:
    * @param CLID of the most-derived @c CondCont.
    * @param id CLID+key for this object.
    * @param proxy @c DataProxy for this object.
+   * @param delfcn Deletion function for the actual payload type.
+   * @param capacity Initial capacity of the container.
    */
   CondCont (Athena::IRCUSvc& rcusvc,
             CLID clid,
             const DataObjID& id,
-            SG::DataProxy* proxy);
+            SG::DataProxy* proxy,
+            const typename CondContSet::delete_function* delfcn,
+            size_t capacity);
 
 
   /**
@@ -779,19 +674,19 @@ protected:
   const void* cast (CLID clid, const void* ptr) const;
 
 
-  /** 
-   * @brief Internal lookup function.
+  /**
+   * @brief Do pointer conversion for the payload type.
    * @param clid CLID for the desired pointer type.
-   * @param t IOV time to find.
-   * @param r If non-null, copy validity range of the object here.
+   * @param ptr Pointer of type @c T*.
    *
-   * Looks up the conditions object corresponding to the IOV time @c t.
-   * If found, convert the pointer to a pointer to the type identified
-   * by CLID and return it.  Otherwise, return nullptr.
+   * Converts @c ptr from @c T* to a pointer to the type
+   * given by @c clid.  Returns nullptr if the conversion
+   * is not possible.
+   *
+   * This is a virtual function that calls @c cast from the most-derived class
+   * of the hierarchy.
    */
-  virtual const void* findByCLID (CLID clid,
-                                  const EventIDBase& t,
-                                  EventIDRange const** r) const override;
+  virtual const void* doCast (CLID clid, const void* ptr) const override;
 
 
 public:
@@ -804,17 +699,10 @@ private:
   typedef CondContBase::RangeKey RangeKey;
 
 
-  /// Sets of mapped objects, by timestamp and run+LBN.
-  typedef CxxUtils::ConcurrentRangeMap<RangeKey, key_type, T, CondContBase::Compare,
-    Athena::RCUUpdater>
-    CondContSet;
-  CondContSet m_condSet_clock, m_condSet_RE;
-
-  /// CLID+key for this container.
-  DataObjID m_id;
-
-  /// Associated @c DataProxy.
-  SG::DataProxy* m_proxy;
+  /// Deletion function to pass to @c ConcurrentRangeMap.
+  static void delfcn (const void* p) {
+    delete reinterpret_cast<const T*>(p);
+  }
 };
 
 
diff --git a/Control/AthenaKernel/AthenaKernel/CondCont.icc b/Control/AthenaKernel/AthenaKernel/CondCont.icc
index 7432e3c35d3ea185fd332571383345267bf9a38c..aac30e77d638ef52450c13565b0977d9ff458aa6 100644
--- a/Control/AthenaKernel/AthenaKernel/CondCont.icc
+++ b/Control/AthenaKernel/AthenaKernel/CondCont.icc
@@ -20,6 +20,49 @@ CLID CondContBase::clid() const
 }
 
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+/**
+ * @brief Return CLID/key corresponding to this container.
+ */
+inline
+const DataObjID& CondContBase::id() const
+{
+  return m_id;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+/**
+ * @brief Return the associated @c DataProxy, if any.
+ */
+inline
+SG::DataProxy* CondContBase::proxy()
+{
+  return m_proxy;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
+/**
+ * @brief Test to see if a given IOV time is mapped in the container.
+ * @param t IOV time to check.
+ */
+inline
+bool CondContBase::valid (const EventIDBase& t) const
+{
+  return findBase (t, nullptr) != nullptr;
+}
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
 /**
  * @brief Do pointer conversion for the payload type.
  * @param clid CLID for the desired pointer type.
@@ -45,6 +88,9 @@ CondContBase::key_type CondContBase::keyFromRunLBN (const EventIDBase& b)
 }
 
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
 /**
  * @brief Make a timestamp key from an EventIDBase.
  * @param Event ID to convert.
@@ -56,6 +102,9 @@ CondContBase::key_type CondContBase::keyFromTimestamp (const EventIDBase& b)
 }
 
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
 /**
  * @brief Default constructor.
  */
@@ -67,6 +116,9 @@ CondContBase::RangeKey::RangeKey()
 }
 
 
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+
 /**
  * @brief Constructor from range+start/stop.
  * @param r The range to store.
@@ -100,11 +152,7 @@ CondCont<T>::CondCont (Athena::IRCUSvc& rcusvc,
                        const DataObjID& id,
                        SG::DataProxy* proxy /*=nullptr*/,
                        size_t capacity /*= 16*/)
-  : Base (rcusvc, ClassID_traits<CondCont<T> >::ID(), id, proxy),
-    m_condSet_clock (typename CondContSet::Updater_t (rcusvc), capacity),
-    m_condSet_RE (typename CondContSet::Updater_t (rcusvc), capacity),
-    m_id (id),
-    m_proxy (proxy)
+  : Base (rcusvc, ClassID_traits<CondCont<T> >::ID(), id, proxy, delfcn, capacity)
 {
   CondCont<T>::registerBaseInit();
 }
@@ -124,171 +172,13 @@ CondCont<T>::~CondCont<T>() {
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 
-/**
- * @brief Return CLID/key corresponding to this container.
- */
-template <class T>
-inline
-const DataObjID& CondCont<T>::id() const
-{
-  return m_id;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Return the associated @c DataProxy, if any.
- */
-template <class T>
-inline
-SG::DataProxy* CondCont<T>::proxy()
-{
-  return m_proxy;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Set the associated @c DataProxy.
- * @param proxy The proxy to set.
- */
-template <class T>
-inline
-void CondCont<T>::setProxy (SG::DataProxy* proxy)
-{
-  m_proxy = proxy;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Dump the container contents for debugging.
- * @param ost Stream to which to write the dump.
- */
-template <typename T>
-void CondCont<T>::list (std::ostream& ost) const
-{
-  ost << "id: " << m_id << "  proxy: " << m_proxy << std::endl;
-
-  ost << "clock: [" << m_condSet_clock.size() << "]" << std::endl;
-  for (const typename CondContSet::value_type& ent : m_condSet_clock.range()) {
-    ost << ent.first.m_range << " " << ent.second << std::endl;
-  }
-  ost << "RE: [" << m_condSet_RE.size() << "]" << std::endl;
-  for (const typename CondContSet::value_type& ent : m_condSet_RE.range()) {
-    ost << ent.first.m_range << " " << ent.second << std::endl;
-  }
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Return the number of conditions objects in the container.
- */
-template <typename T>
-size_t CondCont<T>::entries() const
-{
-  return m_condSet_RE.size() + m_condSet_clock.size();
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Return the number of run+LBN conditions objects
- *        in the container.
- */
-template <typename T>
-size_t CondCont<T>::entriesRunLBN() const
-{
-  return m_condSet_RE.size();
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Return the number of timestamp-based conditions objects
- *        in the container.
- */
-template <typename T>
-size_t CondCont<T>::entriesTimestamp() const
-{
-  return m_condSet_clock.size();
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Return all IOV validity ranges defined in this container.
- */
-template <typename T>
-std::vector<EventIDRange> 
-CondCont<T>::ranges() const
-{
-  std::vector<EventIDRange> r;
-  r.reserve (m_condSet_RE.size() + m_condSet_clock.size());
-  for (const typename CondContSet::value_type& ent : m_condSet_RE.range()) {
-    r.push_back( ent.first.m_range );
-  }
-  for (const typename CondContSet::value_type& ent : m_condSet_clock.range()) {
-    r.push_back( ent.first.m_range );
-  }
-
-  return r;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
 /** 
  * @brief Insert a new conditions object.
  * @param r Range of validity of this object.
- * @param obj Pointer to the object being inserted.
+ * @param t Pointer to the object being inserted.
  * @param ctx Event context for the current thread.
  *
- * @c obj must point to an object of type @c T,
- * except in the case of inheritance, where the type of @c obj must
- * correspond to the most-derived @c CondCont type.
- * The container will take ownership of this object.
- *
- * Returns SUCCESS if the object was successfully inserted;
- * DUPLICATE if the object wasn't inserted because the range
- * duplicates an existing one, and FAILURE otherwise
- * (ownership of the object will be taken in any case).
- */
-template <typename T>
-StatusCode CondCont<T>::typelessInsert (const EventIDRange& r,
-                                        void* obj,
-                                        const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
-{
-  return insert (r, std::unique_ptr<T> (static_cast<T*>(obj)), ctx);
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/** 
- * @brief Insert a new conditions object.
- * @param r Range of validity of this object.
- * @param obj Pointer to the object being inserted.
- * @param ctx Event context for the current thread.
- *
- * @c obj must point to an object of type @c T.
+ * @c t must point to an object of type @c T.
  * This will give an error if this is not called
  * on the most-derived @c CondCont.
  *
@@ -308,39 +198,7 @@ StatusCode CondCont<T>::insert (const EventIDRange& r,
     return StatusCode::FAILURE;
   }
 
-  EventIDBase start = r.start();
-  EventIDBase stop = r.stop();
-
-  // LBN part of ranges may be undefined for an open-ended range.
-  if (start.run_number() != EventIDBase::UNDEFNUM &&
-      stop.run_number() != EventIDBase::UNDEFNUM)
-  {
-    if (!m_condSet_RE.emplace( RangeKey(r,
-                                        CondContBase::keyFromRunLBN (start),
-                                        CondContBase::keyFromRunLBN (stop)),
-                               std::move(t), ctx ))
-    {
-      return CondContStatusCode::DUPLICATE;
-    }
-    
-  }
-  else if (start.isTimeStamp() && stop.isTimeStamp()) {
-    if (!m_condSet_clock.emplace( RangeKey(r,
-                                           CondContBase::keyFromTimestamp (start),
-                                           CondContBase::keyFromTimestamp (stop)),
-                                  std::move(t), ctx ))
-    {
-      return CondContStatusCode::DUPLICATE;
-    }
-  }
-  else {
-    std::cerr << "CondCont<T>::insert error: EventIDRange " << r 
-              << " is neither fully RunEvent nor TimeStamp" 
-              << std::endl;
-    return StatusCode::FAILURE;
-  }
-
-  return this->inserted (ctx);
+  return Base::insertBase (r, std::move(t), ctx);
 }
 
 
@@ -360,75 +218,14 @@ bool CondCont<T>::find (const EventIDBase& t,
                         T const *& obj,
                         EventIDRange const** r) const
 {
-  const void* ptr = findByCLID (ClassID_traits<CondCont<T> >::ID(), t, r);
-  obj = reinterpret_cast<const T*> (ptr);
-  return obj != nullptr;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Test to see if a given IOV time is mapped in the container.
- * @param t IOV time to check.
- */
-template <typename T>
-bool CondCont<T>::valid (const EventIDBase& t) const
-{
-  return findByCLID (this->clid(), t, nullptr) != nullptr;
-}
+  const void* ptr = Base::findBase (t, r);
 
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Return the mapped validity range for an IOV time.
- * @param t IOV time to check.
- * @param r[out] The range containing @c t.
- *
- * Returns true if @c t is mapped; false otherwise.
- */
-template <typename T>
-bool
-CondCont<T>::range(const EventIDBase& t, EventIDRange& r) const
-{
-  const EventIDRange* rp = nullptr;
-  if (findByCLID (this->clid(), t, &rp) != nullptr) {
-    r = *rp;
-    return true;
+  if (ClassID_traits<CondCont<T> >::ID() != this->clid()) {
+    ptr = doCast (ClassID_traits<CondCont<T> >::ID(), ptr);
   }
-  return false;
-}
 
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Remove unused run+LBN entries from the front of the list.
- * @param keys List of keys that may still be in use.
- *             (Must be sorted.)
- *
- * We examine the objects in the container, starting with the earliest one.
- * If none of the keys in @c keys match the range for this object, then
- * it is removed from the container.  We stop when we either find
- * an object with a range matching a key in @c keys or when there
- * is only one object left.
- *
- * The list @c keys should contain keys as computed by keyFromRunLBN.
- * The list must be sorted.
- *
- * Removed objects are queued for deletion once all slots have been
- * marked as quiescent.
- *
- * Returns the number of objects that were removed.
- */
-template <typename T>
-size_t CondCont<T>::trimRunLBN (const std::vector<key_type>& keys)
-{
-  return m_condSet_RE.trim (keys);
+  obj = reinterpret_cast<const T*> (ptr);
+  return obj != nullptr;
 }
 
 
@@ -436,131 +233,22 @@ size_t CondCont<T>::trimRunLBN (const std::vector<key_type>& keys)
 
 
 /**
- * @brief Remove unused timestamp entries from the front of the list.
- * @param keys List of keys that may still be in use.
- *             (Must be sorted.)
- *
- * We examine the objects in the container, starting with the earliest one.
- * If none of the keys in @c keys match the range for this object, then
- * it is removed from the container.  We stop when we either find
- * an object with a range matching a key in @c keys or when there
- * is only one object left.
- *
- * The list @c keys should contain keys as computed by keyFromRunLBN.
- * The list must be sorted.
- *
- * Removed objects are queued for deletion once all slots have been
- * marked as quiescent.
+ * @brief Do pointer conversion for the payload type.
+ * @param clid CLID for the desired pointer type.
+ * @param ptr Pointer of type @c T*.
  *
- * Returns the number of objects that were removed.
- */
-template <typename T>
-size_t CondCont<T>::trimTimestamp (const std::vector<key_type>& keys)
-{
-  return m_condSet_clock.trim (keys);
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Erase the first element not less than @c t.
- * @param IOV time of element to erase.
- * @param ctx Event context for the current thread.
- */
-template <typename T>
-void CondCont<T>::erase (const EventIDBase& t,
-                         const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
-{
-  if (t.isRunLumi()) {
-    m_condSet_RE.erase (CondContBase::keyFromRunLBN (t), ctx);
-  }
-  else if (t.isTimeStamp()) {
-    m_condSet_clock.erase (CondContBase::keyFromTimestamp (t), ctx);
-  }
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Mark that this thread is no longer accessing data from this container.
- * @param ctx Event context for the current thread.
+ * Converts @c ptr from @c T* to a pointer to the type
+ * given by @c clid.  Returns nullptr if the conversion
+ * is not possible.
  *
- * This would normally be done through RCU service.
- * Defined here for purposes of testing.
- */
-template <typename T>
-void
-CondCont<T>::quiescent (const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
-{
-  m_condSet_RE.quiescent (ctx);
-  m_condSet_clock.quiescent (ctx);
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Return the number times an item was inserted into the map.
- */
-template <typename T>
-inline
-size_t CondCont<T>::nInserts() const
-{
-  return m_condSet_clock.nInserts() + m_condSet_RE.nInserts();
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
-/**
- * @brief Return the maximum size of the map.
+ * This is a virtual function that calls @c cast from the most-derived class
+ * of the hierarchy.
  */
 template <typename T>
 inline
-size_t CondCont<T>::maxSize() const
-{
-  return m_condSet_clock.maxSize() + m_condSet_RE.maxSize();
-}
-
-
-/**
- * @brief Extend the range of the last IOV.
- * @param newRange New validity range.
- * @param ctx Event context.
- *
- * Returns failure if the start time of @c newRange does not match the start time
- * of the last IOV in the container.  Otherwise, the end time for the last
- * IOV is changed to the end time for @c newRange.  (If the end time for @c newRange
- * is before the end of the last IOV, then nothing is changed.)
- */
-template <typename T>
-StatusCode
-CondCont<T>::extendLastRange (const EventIDRange& newRange,
-                              const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
+const void* CondCont<T>::doCast (CLID clid, const void* ptr) const
 {
-  typename CondContSet::const_iterator it = nullptr;
-  if (newRange.start().isRunLumi()) {
-    it = m_condSet_RE.extendLastRange
-      (RangeKey (newRange,
-                 CondContBase::keyFromRunLBN (newRange.start()),
-                 CondContBase::keyFromRunLBN (newRange.stop())), ctx);
-  }
-  else if (newRange.start().isTimeStamp()) {
-    it = m_condSet_clock.extendLastRange
-      (RangeKey (newRange,
-                 CondContBase::keyFromTimestamp (newRange.start()),
-                 CondContBase::keyFromTimestamp (newRange.stop())), ctx);
-  }
-  if (it != nullptr) {
-    return StatusCode::SUCCESS;
-  }
-  return StatusCode::FAILURE;
+  return cast (clid, ptr);
 }
 
 
@@ -573,18 +261,18 @@ CondCont<T>::extendLastRange (const EventIDRange& newRange,
  * @param CLID of the most-derived @c CondCont.
  * @param id CLID+key for this object.
  * @param proxy @c DataProxy for this object.
+ * @param delfcn Deletion function for the actual payload type.
+ * @param capacity Initial capacity of the container.
  */
 template <typename T>
 inline
 CondCont<T>::CondCont (Athena::IRCUSvc& rcusvc,
                        CLID clid,
                        const DataObjID& id,
-                       SG::DataProxy* proxy)
-  : Base (rcusvc, clid, id, proxy),
-    m_condSet_clock (typename CondContSet::Updater_t (rcusvc)),
-    m_condSet_RE (typename CondContSet::Updater_t (rcusvc)),
-    m_id(id),
-    m_proxy(proxy)
+                       SG::DataProxy* proxy,
+                       const typename CondContSet::delete_function* delfcn,
+                       size_t capacity)
+  : Base (rcusvc, clid, id, proxy, delfcn, capacity)
 {
   CondCont<T>::registerBaseInit();
 }
@@ -603,6 +291,7 @@ CondCont<T>::CondCont (Athena::IRCUSvc& rcusvc,
  * is not possible.
  */
 template <typename T>
+inline
 const void* CondCont<T>::cast (CLID clid, const void* ptr) const
 {
   if (clid == ClassID_traits<CondCont<T> >::ID())
@@ -615,57 +304,6 @@ const void* CondCont<T>::cast (CLID clid, const void* ptr) const
 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
 
 
-/** 
- * @brief Internal lookup function.
- * @param clid CLID for the desired pointer type.
- * @param t IOV time to find.
- * @param r If non-null, copy validity range of the object here.
- *
- * Looks up the conditions object corresponding to the IOV time @c t.
- * If found, convert the pointer to a pointer to the type identified
- * by CLID and return it.  Otherwise, return nullptr.
- */
-template <typename T>
-const void* CondCont<T>::findByCLID (CLID clid,
-                                     const EventIDBase& t,
-                                     EventIDRange const** r) const
-{
-  const void* ptr = nullptr;
-  if (t.isRunLumi()) {
-    key_type key = CondContBase::keyFromRunLBN (t);
-    typename CondContSet::const_iterator it = m_condSet_RE.find (key);
-    if (it && key < it->first.m_stop) {
-      if (r) {
-        *r = &it->first.m_range;
-      }
-      ptr = it->second;
-    }
-  } 
-
-  if (!ptr && t.isTimeStamp()) {
-    key_type key = CondContBase::keyFromTimestamp (t);
-    typename CondContSet::const_iterator it = m_condSet_clock.find (key);
-    if (it && key < it->first.m_stop) {
-      if (r) {
-        *r = &it->first.m_range;
-      }
-      ptr = it->second;
-    }
-  }
-
-  // Do pointer conversion if needed.
-  if (ptr && clid != ClassID_traits<CondCont<T> >::ID()) {
-    ptr = Base::cast (clid, static_cast<const typename Base::Payload*>
-                              (reinterpret_cast<const T*> (ptr)));
-  }
-
-  return ptr;
-}
-
-
-/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
-
 /**
  * @brief Helper to ensure that the inheritance information for this class
  *               gets initialized.
diff --git a/Control/AthenaKernel/src/CondCont.cxx b/Control/AthenaKernel/src/CondCont.cxx
index 553ae76fb2f115b28e169aa52517c7c59347bbd0..b68ef6806edbde65b49c67d4d875a5e7bf4f905e 100644
--- a/Control/AthenaKernel/src/CondCont.cxx
+++ b/Control/AthenaKernel/src/CondCont.cxx
@@ -63,12 +63,269 @@ bool CondContBase::Category::isDuplicate (StatusCode code)
 STATUSCODE_ENUM_IMPL (CondContStatusCode, CondContBase::Category)
 
 
+/**
+ * @brief Set the associated @c DataProxy.
+ * @param proxy The proxy to set.
+ */
+void CondContBase::setProxy (SG::DataProxy* proxy)
+{
+  m_proxy = proxy;
+}
+
+
+/**
+ * @brief Dump the container contents for debugging.
+ * @param ost Stream to which to write the dump.
+ */
+void CondContBase::list (std::ostream& ost) const
+{
+  ost << "id: " << m_id << "  proxy: " << m_proxy << std::endl;
+
+  ost << "clock: [" << m_condSet_clock.size() << "]" << std::endl;
+  for (const typename CondContSet::value_type& ent : m_condSet_clock.range()) {
+    ost << ent.first.m_range << " " << ent.second << std::endl;
+  }
+  ost << "RE: [" << m_condSet_RE.size() << "]" << std::endl;
+  for (const typename CondContSet::value_type& ent : m_condSet_RE.range()) {
+    ost << ent.first.m_range << " " << ent.second << std::endl;
+  }
+}
+
+
 /**
  * @brief Dump the container to cout.  For calling from the debugger.
  */
 void CondContBase::print() const
 {
-  this->list (std::cout);
+  list (std::cout);
+}
+
+
+/**
+ * @brief Return the number of conditions objects in the container.
+ */
+size_t CondContBase::entries() const
+{
+  return m_condSet_RE.size() + m_condSet_clock.size();
+}
+
+
+/**
+ * @brief Return the number of run+LBN conditions objects
+ *        in the container.
+ */
+size_t CondContBase::entriesRunLBN() const
+{
+  return m_condSet_RE.size();
+}
+
+
+/**
+ * @brief Return the number of timestamp-based conditions objects
+ *        in the container.
+ */
+size_t CondContBase::entriesTimestamp() const
+{
+  return m_condSet_clock.size();
+}
+
+
+/**
+ * @brief Return all IOV validity ranges defined in this container.
+ */
+std::vector<EventIDRange> 
+CondContBase::ranges() const
+{
+  std::vector<EventIDRange> r;
+  r.reserve (m_condSet_RE.size() + m_condSet_clock.size());
+  for (const typename CondContSet::value_type& ent : m_condSet_RE.range()) {
+    r.push_back( ent.first.m_range );
+  }
+  for (const typename CondContSet::value_type& ent : m_condSet_clock.range()) {
+    r.push_back( ent.first.m_range );
+  }
+
+  return r;
+}
+
+
+/** 
+ * @brief Insert a new conditions object.
+ * @param r Range of validity of this object.
+ * @param obj Pointer to the object being inserted.
+ * @param ctx Event context for the current thread.
+ *
+ * @c obj must point to an object of type @c T,
+ * except in the case of inheritance, where the type of @c obj must
+ * correspond to the most-derived @c CondCont type.
+ * The container will take ownership of this object.
+ *
+ * Returns SUCCESS if the object was successfully inserted;
+ * DUPLICATE if the object wasn't inserted because the range
+ * duplicates an existing one, and FAILURE otherwise
+ * (ownership of the object will be taken in any case).
+ */
+StatusCode
+CondContBase::typelessInsert (const EventIDRange& r,
+                              void* obj,
+                              const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
+{
+  return insertBase (r,
+                     CondContSet::payload_unique_ptr (obj, m_condSet_RE.delfcn()),
+                     ctx);
+}
+
+
+/**
+ * @brief Return the mapped validity range for an IOV time.
+ * @param t IOV time to check.
+ * @param r[out] The range containing @c t.
+ *
+ * Returns true if @c t is mapped; false otherwise.
+ */
+bool
+CondContBase::range(const EventIDBase& t, EventIDRange& r) const
+{
+  const EventIDRange* rp = nullptr;
+  if (findBase (t, &rp) != nullptr) {
+    r = *rp;
+    return true;
+  }
+  return false;
+}
+
+
+/**
+ * @brief Erase the first element not less than @c t.
+ * @param IOV time of element to erase.
+ * @param ctx Event context for the current thread.
+ */
+void CondContBase::erase (const EventIDBase& t,
+                          const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
+{
+  if (t.isRunLumi()) {
+    m_condSet_RE.erase (CondContBase::keyFromRunLBN (t), ctx);
+  }
+  else if (t.isTimeStamp()) {
+    m_condSet_clock.erase (CondContBase::keyFromTimestamp (t), ctx);
+  }
+}
+
+
+/**
+ * @brief Remove unused run+LBN entries from the front of the list.
+ * @param keys List of keys that may still be in use.
+ *             (Must be sorted.)
+ *
+ * We examine the objects in the container, starting with the earliest one.
+ * If none of the keys in @c keys match the range for this object, then
+ * it is removed from the container.  We stop when we either find
+ * an object with a range matching a key in @c keys or when there
+ * is only one object left.
+ *
+ * The list @c keys should contain keys as computed by keyFromRunLBN.
+ * The list must be sorted.
+ *
+ * Removed objects are queued for deletion once all slots have been
+ * marked as quiescent.
+ *
+ * Returns the number of objects that were removed.
+ */
+size_t CondContBase::trimRunLBN (const std::vector<key_type>& keys)
+{
+  return m_condSet_RE.trim (keys);
+}
+
+
+/**
+ * @brief Remove unused timestamp entries from the front of the list.
+ * @param keys List of keys that may still be in use.
+ *             (Must be sorted.)
+ *
+ * We examine the objects in the container, starting with the earliest one.
+ * If none of the keys in @c keys match the range for this object, then
+ * it is removed from the container.  We stop when we either find
+ * an object with a range matching a key in @c keys or when there
+ * is only one object left.
+ *
+ * The list @c keys should contain keys as computed by keyFromRunLBN.
+ * The list must be sorted.
+ *
+ * Removed objects are queued for deletion once all slots have been
+ * marked as quiescent.
+ *
+ * Returns the number of objects that were removed.
+ */
+size_t CondContBase::trimTimestamp (const std::vector<key_type>& keys)
+{
+  return m_condSet_clock.trim (keys);
+}
+
+
+/**
+ * @brief Mark that this thread is no longer accessing data from this container.
+ * @param ctx Event context for the current thread.
+ *
+ * This would normally be done through RCU service.
+ * Defined here for purposes of testing.
+ */
+void
+CondContBase::quiescent (const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
+{
+  m_condSet_RE.quiescent (ctx);
+  m_condSet_clock.quiescent (ctx);
+}
+
+
+/**
+ * @brief Return the number times an item was inserted into the map.
+ */
+size_t CondContBase::nInserts() const
+{
+  return m_condSet_clock.nInserts() + m_condSet_RE.nInserts();
+}
+
+
+/**
+ * @brief Return the maximum size of the map.
+ */
+size_t CondContBase::maxSize() const
+{
+  return m_condSet_clock.maxSize() + m_condSet_RE.maxSize();
+}
+
+
+/**
+ * @brief Extend the range of the last IOV.
+ * @param newRange New validity range.
+ * @param ctx Event context.
+ *
+ * Returns failure if the start time of @c newRange does not match the start time
+ * of the last IOV in the container.  Otherwise, the end time for the last
+ * IOV is changed to the end time for @c newRange.  (If the end time for @c newRange
+ * is before the end of the last IOV, then nothing is changed.)
+ */
+StatusCode
+CondContBase::extendLastRange (const EventIDRange& newRange,
+                               const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
+{
+  typename CondContSet::const_iterator it = nullptr;
+  if (newRange.start().isRunLumi()) {
+    it = m_condSet_RE.extendLastRange
+      (RangeKey (newRange,
+                 CondContBase::keyFromRunLBN (newRange.start()),
+                 CondContBase::keyFromRunLBN (newRange.stop())), ctx);
+  }
+  else if (newRange.start().isTimeStamp()) {
+    it = m_condSet_clock.extendLastRange
+      (RangeKey (newRange,
+                 CondContBase::keyFromTimestamp (newRange.start()),
+                 CondContBase::keyFromTimestamp (newRange.stop())), ctx);
+  }
+  if (it != nullptr) {
+    return StatusCode::SUCCESS;
+  }
+  return StatusCode::FAILURE;
 }
 
 
@@ -78,12 +335,20 @@ void CondContBase::print() const
  * @param CLID of the most-derived @c CondCont.
  * @param id CLID+key for this object.
  * @param proxy @c DataProxy for this object.
+ * @param delfcn Deletion function for the actual payload type.
+ * @param capacity Initial capacity of the container.
  */
-CondContBase::CondContBase (Athena::IRCUSvc& /*rcusvc*/,
+CondContBase::CondContBase (Athena::IRCUSvc& rcusvc,
                             CLID clid,
-                            const DataObjID& /*id*/,
-                            SG::DataProxy* /*proxy*/)
+                            const DataObjID& id,
+                            SG::DataProxy* proxy,
+                            CondContSet::delete_function* delfcn,
+                            size_t capacity)
   : m_clid (clid),
+    m_id (id),
+    m_proxy (proxy),
+    m_condSet_clock (Updater_t (rcusvc, delfcn), delfcn, capacity),
+    m_condSet_RE (Updater_t (rcusvc, delfcn), delfcn, capacity),
     m_cleanerSvc (s_cleanerSvcName, "CondContBase")
 {
   if (!m_cleanerSvc.retrieve().isSuccess()) {
@@ -92,6 +357,98 @@ CondContBase::CondContBase (Athena::IRCUSvc& /*rcusvc*/,
 }
 
 
+/** 
+ * @brief Insert a new conditions object.
+ * @param r Range of validity of this object.
+ * @param t Pointer to the object being inserted.
+ * @param ctx Event context for the current thread.
+ *
+ * Returns SUCCESS if the object was successfully inserted;
+ * DUPLICATE if the object wasn't inserted because the range
+ * duplicates an existing one, and FAILURE otherwise
+ * (ownership of the object will be taken in any case).
+ */
+StatusCode
+CondContBase::insertBase (const EventIDRange& r,
+                          CondContSet::payload_unique_ptr t,
+                          const EventContext& ctx /*= Gaudi::Hive::currentContext()*/)
+{
+  EventIDBase start = r.start();
+  EventIDBase stop = r.stop();
+
+  // LBN part of ranges may be undefined for an open-ended range.
+  if (start.run_number() != EventIDBase::UNDEFNUM &&
+      stop.run_number() != EventIDBase::UNDEFNUM)
+  {
+    if (!m_condSet_RE.emplace( RangeKey(r,
+                                        keyFromRunLBN (start),
+                                        keyFromRunLBN (stop)),
+                               std::move(t), ctx ))
+    {
+      return CondContStatusCode::DUPLICATE;
+    }
+    
+  }
+  else if (start.isTimeStamp() && stop.isTimeStamp()) {
+    if (!m_condSet_clock.emplace( RangeKey(r,
+                                           keyFromTimestamp (start),
+                                           keyFromTimestamp (stop)),
+                                  std::move(t), ctx ))
+    {
+      return CondContStatusCode::DUPLICATE;
+    }
+  }
+  else {
+    std::cerr << "CondCont<T>::insert error: EventIDRange " << r 
+              << " is neither fully RunEvent nor TimeStamp" 
+              << std::endl;
+    return StatusCode::FAILURE;
+  }
+
+  return this->inserted (ctx);
+}
+
+
+/** 
+ * @brief Internal lookup function.
+ * @param clid CLID for the desired pointer type.
+ * @param t IOV time to find.
+ * @param r If non-null, copy validity range of the object here.
+ *
+ * Looks up the conditions object corresponding to the IOV time @c t.
+ * If found, convert the pointer to a pointer to the type identified
+ * by CLID and return it.  Otherwise, return nullptr.
+ */
+const void* CondContBase::findBase (const EventIDBase& t,
+                                    EventIDRange const** r) const
+{
+  const void* ptr = nullptr;
+  if (t.isRunLumi()) {
+    key_type key = keyFromRunLBN (t);
+    CondContSet::const_iterator it = m_condSet_RE.find (key);
+    if (it && key < it->first.m_stop) {
+      if (r) {
+        *r = &it->first.m_range;
+      }
+      ptr = it->second;
+    }
+  } 
+
+  if (!ptr && t.isTimeStamp()) {
+    key_type key = keyFromTimestamp (t);
+    CondContSet::const_iterator it = m_condSet_clock.find (key);
+    if (it && key < it->first.m_stop) {
+      if (r) {
+        *r = &it->first.m_range;
+      }
+      ptr = it->second;
+    }
+  }
+
+  return ptr;
+}
+
+
 /**
  * @brief Tell the cleaner that a new object was added to the container.
  */
diff --git a/Control/AthenaServices/test/ConditionsCleanerSvc_test.cxx b/Control/AthenaServices/test/ConditionsCleanerSvc_test.cxx
index 48266d65f87e3fd7efc25d9df9029b28f37e92fd..701975f22e2ef12ab3b89f24c19f2e6a0e90b041 100644
--- a/Control/AthenaServices/test/ConditionsCleanerSvc_test.cxx
+++ b/Control/AthenaServices/test/ConditionsCleanerSvc_test.cxx
@@ -27,9 +27,9 @@ class RCUTest
   : public Athena::IRCUSvc
 {
 public:
-  virtual void add (Athena::IRCUObject*) override { std::abort(); }
-  virtual StatusCode remove (Athena::IRCUObject*) override { std::abort(); }
-  virtual size_t getNumSlots() const override { std::abort(); }
+  virtual void add (Athena::IRCUObject*) override {  }
+  virtual StatusCode remove (Athena::IRCUObject*) override { return StatusCode::SUCCESS; }
+  virtual size_t getNumSlots() const override { return 1; }
   virtual unsigned long addRef()override { std::abort(); }
   virtual unsigned long release() override { std::abort(); }
   virtual StatusCode queryInterface( const InterfaceID&, void** ) override { std::abort(); }
@@ -41,39 +41,12 @@ class CondContTest
   : public CondContBase
 {
 public:
-  CondContTest()
-    : CondContBase (m_rcu, 123, m_id, nullptr)
+  CondContTest (Athena::IRCUSvc& rcusvc, const DataObjID& id)
+    : CondContBase (rcusvc, 123, id, nullptr, nullptr, 0)
   {}
-  virtual const DataObjID& id() const override { std::abort(); }
-  virtual SG::DataProxy* proxy() override { std::abort(); }
-  virtual void setProxy(SG::DataProxy*) override { std::abort(); }
-  virtual void list (std::ostream&) const override { std::abort(); }
-  virtual size_t entries() const override { std::abort(); }
-  virtual size_t entriesRunLBN() const override { std::abort(); }
-  virtual size_t entriesTimestamp() const override { std::abort(); }
-  virtual std::vector<EventIDRange> ranges() const override { std::abort(); }
-  virtual StatusCode typelessInsert (const EventIDRange&,
-                                     void*,
-                                     const EventContext&) override { std::abort(); }
-  virtual bool valid( const EventIDBase&) const override { std::abort(); }
-  virtual bool range (const EventIDBase&, EventIDRange&) const override { std::abort(); }
-  virtual void erase (const EventIDBase&,
-                      const EventContext&) override { std::abort(); }
-  virtual size_t trimRunLBN (const std::vector<key_type>&) override { std::abort(); }
-  virtual size_t trimTimestamp (const std::vector<key_type>&) override { std::abort(); }
-  virtual void quiescent (const EventContext&) override { std::abort(); }
-  
-  virtual const void* findByCLID (CLID,
-                                  const EventIDBase&,
-                                  EventIDRange const**) const override { std::abort(); }
-  virtual size_t nInserts() const override { std::abort(); }
-  virtual size_t maxSize() const override { std::abort(); }
-  virtual StatusCode extendLastRange (const EventIDRange& /*newRange*/,
-                                      const EventContext& /*ctx*/) override { std::abort(); }
-
-private:
-  RCUTest m_rcu;
-  DataObjID m_id;
+
+  virtual const void* doCast (CLID /*clid*/, const void* /*ptr*/) const override
+  { std::abort(); }
 };
 
 
@@ -131,7 +104,10 @@ void testit (IService* mgr)
 
   assert( ccs->event (EventContext(0,0), false).isSuccess() );
 
-  CondContTest cc;
+  RCUTest rcu;
+  DataObjID id;
+
+  CondContTest cc (rcu, id);
 
   assert( ccs->condObjAdded (EventContext(0,0), cc).isSuccess() );
   assert( ccs->printStats().isSuccess() );
diff --git a/Control/AthenaServices/test/DelayedConditionsCleanerSvc_test.cxx b/Control/AthenaServices/test/DelayedConditionsCleanerSvc_test.cxx
index fc52680c26e283914449b7fbfdb7d1c84339156b..a3b29ee45f6130d6cbd35984aeba326ba3a80934 100644
--- a/Control/AthenaServices/test/DelayedConditionsCleanerSvc_test.cxx
+++ b/Control/AthenaServices/test/DelayedConditionsCleanerSvc_test.cxx
@@ -37,9 +37,9 @@ class RCUTest
   : public Athena::IRCUSvc
 {
 public:
-  virtual void add (Athena::IRCUObject*) override { std::abort(); }
-  virtual StatusCode remove (Athena::IRCUObject*) override { std::abort(); }
-  virtual size_t getNumSlots() const override { std::abort(); }
+  virtual void add (Athena::IRCUObject*) override {  }
+  virtual StatusCode remove (Athena::IRCUObject*) override { return StatusCode::SUCCESS; }
+  virtual size_t getNumSlots() const override { return 1; }
   virtual unsigned long addRef()override { std::abort(); }
   virtual unsigned long release() override { std::abort(); }
   virtual StatusCode queryInterface( const InterfaceID&, void** ) override { std::abort(); }
@@ -51,34 +51,13 @@ class CondContTest
   : public CondContBase
 {
 public:
-  CondContTest (int nlbn, int nts)
-    : CondContBase (m_rcu, 123, m_id, nullptr),
+  CondContTest (Athena::IRCUSvc& rcusvc, const DataObjID& id, int nlbn, int nts)
+    : CondContBase (rcusvc, 123, id, nullptr, nullptr, 0),
       m_nlbn(nlbn), m_nts (nts)
   {}
   
-  virtual const DataObjID& id() const override { std::abort(); }
-  virtual SG::DataProxy* proxy() override { std::abort(); }
-  virtual void setProxy(SG::DataProxy*) override { std::abort(); }
-  virtual void list (std::ostream&) const override { std::abort(); }
-  virtual size_t entries() const override { std::abort(); }
-  virtual std::vector<EventIDRange> ranges() const override { std::abort(); }
-  virtual StatusCode typelessInsert (const EventIDRange&,
-                                     void*,
-                                     const EventContext& = Gaudi::Hive::currentContext()) override { std::abort(); }
-  virtual bool valid( const EventIDBase&) const override { std::abort(); }
-  virtual bool range (const EventIDBase&, EventIDRange&) const override { std::abort(); }
-  virtual void erase (const EventIDBase&,
-                      const EventContext& = Gaudi::Hive::currentContext()) override { std::abort(); }
-  virtual void quiescent (const EventContext& = Gaudi::Hive::currentContext()) override { std::abort(); }
-  virtual const void* findByCLID (CLID,
-                                  const EventIDBase&,
-                                  EventIDRange const**) const override { std::abort(); }
-
-  virtual size_t nInserts() const override { return 0; }
-  virtual size_t maxSize() const override { return 0; }
-  virtual StatusCode extendLastRange (const EventIDRange& /*newRange*/,
-                                      const EventContext& /*ctx*/) override { std::abort(); }
-
+  virtual const void* doCast (CLID /*clid*/, const void* /*ptr*/) const override
+  { std::abort(); }
   
   virtual size_t entriesRunLBN() const override
   {
@@ -120,8 +99,6 @@ public:
 
   
 private:
-  RCUTest m_rcu;
-  DataObjID m_id;
   int m_nlbn;
   int m_nts;
   std::list<std::vector<key_type> > m_keysRunLBN;
@@ -151,9 +128,12 @@ void test1 (Athena::IConditionsCleanerSvc& svc)
 {
   typedef CondContBase::key_type key_type;
 
+  RCUTest rcu;
+  DataObjID id;
+
   std::cout << "test1\n";
-  CondContTest cc1 (10, 0);
-  CondContTest cc2 (0, 10);
+  CondContTest cc1 (rcu, id, 10, 0);
+  CondContTest cc2 (rcu, id, 0, 10);
 
   assert( svc.event (makeCtx(0), false).isSuccess() );
   assert( svc.event (makeCtx(1), false).isSuccess() );
@@ -188,8 +168,8 @@ void test1 (Athena::IConditionsCleanerSvc& svc)
   assert (cc2.nkeysTimestamp() == 0);
   assert (cc1.keysRunLBN() == (std::vector<key_type> { 0, 1001, 1003, 1004, 1201, 1301 }));
 
-  CondContTest cc3 (10, 0);
-  CondContTest cc4 (0, 10);
+  CondContTest cc3 (rcu, id, 10, 0);
+  CondContTest cc4 (rcu, id, 0, 10);
 
   assert( svc.condObjAdded (makeCtx(300), cc1).isSuccess() );
   assert( svc.condObjAdded (makeCtx(303), cc2).isSuccess() );
diff --git a/Control/CxxUtils/CxxUtils/ConcurrentRangeMap.h b/Control/CxxUtils/CxxUtils/ConcurrentRangeMap.h
index 268b81c12f6129f9f857bf9494e192ce2729478d..7e98bfbcd58b592d7f802ca11caaa0de40f0eb11 100644
--- a/Control/CxxUtils/CxxUtils/ConcurrentRangeMap.h
+++ b/Control/CxxUtils/CxxUtils/ConcurrentRangeMap.h
@@ -37,6 +37,17 @@ namespace CxxUtils {
  * first for which the range is not less than the key.  We also
  * support insertions, erasures, and iteration.
  *
+ * The only thing we need to do with the contained pointers is to delete them.
+ * Rather than doing that directly, we take a deletion function as an argument
+ * to the constructor; this should be a void function that takes a pointer argument
+ * and deletes it.  This allows one to instantiate this template with @c void as @c T,
+ * to reduce the amount of generated code.  The @c emplace method takes
+ * a @c unique_ptr as an argument.  We define @c payload_unique_ptr which
+ * is a @c unique_ptr to @c T that does deletion by calling an arbitrary function.
+ * @c payload_unique_ptr may be initialized from a @c unique_ptr; to construct
+ * one directly, the deletion function should be passed as a second argument
+ * to the @c payload_unique_ptr constructor.
+ *
  * There can be only one writer at a time; this is enforced with internal locks.
  * However, there can be any number of concurrent readers at any time.
  * The reads are lockless (but not necessarily waitless, though this should
@@ -116,6 +127,69 @@ public:
   typedef COMPARE key_compare;
   typedef KEY key_query_type;
 
+
+  /// Function to delete a @c T*
+  typedef void delete_function (const T*);
+
+
+  /**
+   * @brief @c unique_ptr deletion class for a payload object.
+   *
+   * We can't use the unique_ptr default because we want to allow
+   * instantiating with a @c void.
+   */
+  struct DeletePayload
+  {
+    /// Initialize with an explicit deletion function.
+    DeletePayload (delete_function* delfcn)
+      : m_delete (delfcn)
+    {
+    }
+
+    /// Allow initializing a @c payload_unique_ptr from a @c std::unique_ptr<U>.
+    template <class U>
+    static void delfcn (const T* p)
+    {
+      delete reinterpret_cast<const U*>(p);
+    }
+    template <class U>
+    DeletePayload (const std::default_delete<U>&)
+    {
+      m_delete = delfcn<U>;
+    }
+
+    /// Delete a pointer.
+    void operator() (const T* p) const
+    {
+      m_delete (p);
+    }
+
+    /// The deletion function.
+    delete_function* m_delete;
+  };
+
+
+  /**
+   * @brief @c unique_ptr holding a payload object.
+   *
+   * One may initialize an instance of this in one of two ways.
+   * First, from another @c std::unique_ptr:
+   *
+   *@code
+   *   payload_unique_ptr p = std::unique_ptr<U> (...);
+   @endcode
+   *
+   * where U* must be convertable to T*.  In this case, the pointer
+   * will be deleted as a U*.
+   * Second, one can supply an explicit deletion function:
+   *
+   *@code
+   *   T* tp = ...;
+   *   payload_unique_ptr p (tp, delfcn);
+   @endcode
+   */
+  typedef std::unique_ptr<T, DeletePayload> payload_unique_ptr;
+
   typedef const value_type* const_iterator;
   typedef boost::iterator_range<const_iterator> const_iterator_range;
 
@@ -138,9 +212,11 @@ public:
   public:
     /**
      * @brief Constructor.
+     * @param delfcn Deletion function.
      * @param capacity Size of the data vector to allocate.
      */
-    Impl (size_t capacity = 10);
+    Impl (delete_function* delfcn,
+          size_t capacity = 10);
 
 
     /**
@@ -170,6 +246,9 @@ public:
 
 
   private:
+    /// Deletion function.
+    delete_function* m_delete;
+
     /// Vector holding the map data.
     std::vector<value_type> m_data;
 
@@ -185,10 +264,12 @@ public:
    * @brief Constructor.
    * @param updater Object used to manage memory
    *                (see comments at the start of the class).
+   * @param delfcn Deletion function.
    * @param capacity Initial capacity of the map.
    * @param compare Comparison object.
    */
   ConcurrentRangeMap (Updater_t&& updater,
+                      delete_function* delfcn,
                       size_t capacity = 16,
                       const COMPARE& compare = COMPARE());
 
@@ -220,7 +301,7 @@ public:
    * no new element is inserted (and @c ptr gets deleted).
    */
   bool emplace (const RANGE& range,
-                std::unique_ptr<T> ptr,
+                payload_unique_ptr ptr,
                 const typename Updater_t::Context_t& ctx =
                   Updater_t::defaultContext());
 
@@ -335,6 +416,12 @@ public:
                     Updater_t::defaultContext());
 
 
+  /**
+   * @brief Return the deletion function for this container.
+   */
+  delete_function* delfcn() const;
+
+
 private:
   /**
    * @brief Return the begin/last pointers.
@@ -393,6 +480,9 @@ private:
   /// Comparison object.
   COMPARE m_compare;
 
+  /// Deletion function.
+  delete_function* m_delete;
+
   /// Current version of the implementation class.
   Impl* m_impl;
 
diff --git a/Control/CxxUtils/CxxUtils/ConcurrentRangeMap.icc b/Control/CxxUtils/CxxUtils/ConcurrentRangeMap.icc
index 094c98684f8cb37da50a328fe61625d5419fccab..95dd7251494b5a54e149a193fbe918ee6d869d16 100644
--- a/Control/CxxUtils/CxxUtils/ConcurrentRangeMap.icc
+++ b/Control/CxxUtils/CxxUtils/ConcurrentRangeMap.icc
@@ -19,11 +19,14 @@ namespace CxxUtils {
 
 /**
  * @brief Constructor.
+ * @param delfcn Deletion function.
  * @param capacity Size of the data vector to allocate.
  */
 T_CONCURRENTRANGEMAP
-CONCURRENTRANGEMAP::Impl::Impl (size_t capacity /*= 10*/)
-  : m_data (capacity)
+CONCURRENTRANGEMAP::Impl::Impl (delete_function* delfcn,
+                                size_t capacity /*= 10*/)
+  : m_delete (delfcn),
+    m_data (capacity)
 {
 }
 
@@ -37,7 +40,7 @@ T_CONCURRENTRANGEMAP
 CONCURRENTRANGEMAP::Impl::~Impl()
 {
   for (const T* p : m_garbage) {
-    delete p;
+    m_delete (p);
   }
 }
 
@@ -79,19 +82,23 @@ CONCURRENTRANGEMAP::Impl::discard (const T* p)
  * @brief Constructor.
  * @param updater Object used to manage memory
  *                (see comments at the start of the class).
+ * @param delfcn Deletion function.
  * @param capacity Initial capacity of the map.
  * @param compare Comparison object.
  */
 T_CONCURRENTRANGEMAP
 CONCURRENTRANGEMAP::ConcurrentRangeMap (Updater_t&& updater,
+                                        delete_function* delfcn,
                                         size_t capacity /*= 16*/,
                                         const COMPARE& compare /*= COMPARE()*/)
+
   : m_updater (std::move (updater)),
     m_compare (compare),
+    m_delete (delfcn),
     m_nInserts (0),
     m_maxSize (0)
 {
-  auto impl = std::make_unique<Impl> (capacity);
+  auto impl = std::make_unique<Impl> (m_delete, capacity);
   value_type* data = impl->data();
   installImpl (std::move (impl),
                data, data,
@@ -110,7 +117,7 @@ CONCURRENTRANGEMAP::~ConcurrentRangeMap()
   value_type* last = m_last;
   if (last) {
     for (value_type* p = m_begin; p <= m_last; ++p) {
-      delete p->second;
+      m_delete (p->second);
     }
       
   }
@@ -165,7 +172,7 @@ CONCURRENTRANGEMAP::find (const key_query_type& key) const
 T_CONCURRENTRANGEMAP
 bool
 CONCURRENTRANGEMAP::emplace (const RANGE& range,
-                             std::unique_ptr<T> ptr,
+                             payload_unique_ptr ptr,
                              const typename Updater_t::Context_t& ctx
                                /*= Updater_t::defaultContext()*/)
 {
@@ -209,7 +216,7 @@ CONCURRENTRANGEMAP::emplace (const RANGE& range,
   }
 
   // Allocate the new object.
-  auto new_impl = std::make_unique<Impl> (new_capacity);
+  auto new_impl = std::make_unique<Impl> (m_delete, new_capacity);
   value_type* new_begin = new_impl->data();
   value_type* new_end = new_begin;
 
@@ -293,7 +300,7 @@ CONCURRENTRANGEMAP::erase (const key_query_type& key,
 
   // Need to make a new implementation object and copy data.
   size_t capacity = m_impl->capacity();
-  auto new_impl = std::make_unique<Impl> (capacity);
+  auto new_impl = std::make_unique<Impl> (m_delete, capacity);
   value_type* new_begin = new_impl->data();
   value_type* new_end = new_begin;
 
@@ -355,7 +362,7 @@ CONCURRENTRANGEMAP::extendLastRange (const RANGE& newRange,
 
   // Make a new implementation object and copy data.
   size_t capacity = m_impl->capacity();
-  auto new_impl = std::make_unique<Impl> (capacity);
+  auto new_impl = std::make_unique<Impl> (m_delete, capacity);
   value_type* new_begin = new_impl->data();
   value_type* new_end = new_begin;
   value_type* begin = m_begin;
@@ -426,7 +433,7 @@ CONCURRENTRANGEMAP::trim (const std::vector<key_query_type>& keys)
 
     if (!garbage) {
       // Allocate a dummy impl object if we haven't done so yet.
-      garbage = std::make_unique<Impl> (0);
+      garbage = std::make_unique<Impl> (m_delete, 0);
     }
 
     // Add it to the garbage list.
@@ -534,6 +541,18 @@ CONCURRENTRANGEMAP::quiescent (const typename Updater_t::Context_t& ctx /*= Upda
 }
 
 
+/**
+ * @brief Return the deletion function for this container.
+ */
+T_CONCURRENTRANGEMAP
+inline
+typename CONCURRENTRANGEMAP::delete_function*
+CONCURRENTRANGEMAP::delfcn() const
+{
+  return m_delete;
+}
+
+
 /**
  * @brief Return the begin/last pointers.
  * @param [inout] last Current value of last.
diff --git a/Control/CxxUtils/test/ConcurrentRangeMap_test.cxx b/Control/CxxUtils/test/ConcurrentRangeMap_test.cxx
index 3fbaabec2322f365cd91ba614592953a1577c820..abfdfc1d9a0be13c272adf4c6d351917379aadac 100644
--- a/Control/CxxUtils/test/ConcurrentRangeMap_test.cxx
+++ b/Control/CxxUtils/test/ConcurrentRangeMap_test.cxx
@@ -76,6 +76,12 @@ struct Payload
 };
 
 
+void delfcn (const Payload* p)
+{
+  delete p;
+}
+
+
 template <class T>
 class TestUpdater
 {
@@ -152,7 +158,7 @@ void test1a()
   std::cout << "test1a\n";
   Payload::Hist phist;
   {
-    TestMap map (TestMap::Updater_t(), 3);
+    TestMap map (TestMap::Updater_t(), delfcn, 3);
 
     assert (map.size() == 0);
     assert (map.empty());
@@ -555,7 +561,7 @@ void test1b()
   std::cout << "test1b\n";
 
   Payload::Hist phist;
-  TestMap map (TestMap::Updater_t(), 100);
+  TestMap map (TestMap::Updater_t(), delfcn, 100);
   assert (map.emplace (Range (10, 20), std::make_unique<Payload> (100, &phist)));
   assert (map.emplace (Range (25, 30), std::make_unique<Payload> (200, &phist)));
   assert (map.emplace (Range (30, 40), std::make_unique<Payload> (300, &phist)));
@@ -785,7 +791,7 @@ void test2_Reader::operator()()
 
 void test2_iter()
 {
-  TestMap map (TestMap::Updater_t(), 20);
+  TestMap map (TestMap::Updater_t(), delfcn, 20);
 
   const int nthread = 4;
   std::thread threads[nthread];