views:

80

answers:

2

Hi all,

In my continuing adventure with templates, I've templated my Container class not just on the ItemType it holds, but also on a Functor argument that determines how it should order the items. So far, so good.

A little problem I've run into occurs when I want to copy the contents of one Container to another: If the two Containers have different Functor types, then they technically are unrelated classes. Therefore, Container A isn't allowed to access the non-public contents of Container B. Is there any good way to deal with this problem, other than making everything I need to access public? Some way to template a "friend" declaration, perhaps?

Example code to demonstrate the problem follows:

#include <stdio.h>

class FunctorA {};
class FunctorB {};

template <class ItemType, class Functor> class MyContainer
{
public:
   MyContainer() : _metaData(0) {/* empty */}

   template<class RHSFunctor> void CopyFrom(const MyContainer<ItemType, RHSFunctor> & copyFrom)
   {
      _metaData = copyFrom._metaData;
      _item     = copyFrom._item;
   }

private:
  int _metaData;
  ItemType _item;
};

int main(int argc, char ** argv)
{
   MyContainer<void *, FunctorA> containerA;
   MyContainer<void *, FunctorB> containerB;

   containerA.CopyFrom(containerB);  // error, containerA::CopyFrom() can't access containerB's private data!
   return 0;
}
+2  A: 

You can make a base template class templated just on ItemType, keep the data there, have the full-fledged 2-args template subclass that base, AND put the copy-from in the base class as it doesn't depend on the functor anyway. I.e.:

template <class ItemType> class MyContainerBase
{
public:
   MyContainerBase() : _metaData(0) {/* empty */}

   void CopyFrom(const MyContainerBase<ItemType> & copyFrom)
   {
      _metaData = copyFrom._metaData;
      _item     = copyFrom._item;
   }

protected:
  int _metaData;
  ItemType _item;
};

template <class ItemType, class Functor> class MyContainer:
    public MyContainerBase<ItemType>
{
  // whatever you need here -- I made the data above protected
  // just on the assumption you may need to access it here;-)
};
Alex Martelli
Jeremy, this technique, putting a lot of behavior in a base class that has fewer template arguments (sometimes none) and then adding specific extra features in a subclass is a very general technique and is used a lot. (The GNU libstdc++ has numerous cases that are worth looking at.)
quark
Awesome, that's just what was I was looking for. Thanks!
Jeremy Friesner
+1  A: 

As you point out, you can also use a friend function:

class FunctorA {};
class FunctorB {};

template <class ItemType, class Functor> class MyContainer
{
public:
  MyContainer() : _metaData(0) {/* empty */}

  template<class CmnItemType, class LHSFunctor, class RHSFunctor>
  friend void Copy(const MyContainer<CmnItemType, LHSFunctor> & copyFrom
    , MyContainer<CmnItemType, RHSFunctor> & copyTo);

private:
  int _metaData;
  ItemType _item;
};

template<class CmnItemType, class LHSFunctor, class RHSFunctor>
void Copy(const MyContainer<CmnItemType, LHSFunctor> & copyFrom
  , MyContainer<CmnItemType, RHSFunctor> & copyTo)
{
  copyTo._metaData = copyFrom._metaData;
  copyTo._item     = copyFrom._item;
}


int main(int argc, char ** argv)
{
  MyContainer<void *, FunctorA> containerA;
  MyContainer<void *, FunctorB> containerB;

  Copy(containerB, containerA);
  return 0;
}
Richard Corden
That's a good idea also, thanks!
Jeremy Friesner