7
  template<class Key1, class Key2, class Type> class DualMultimapCache
  {
  public:
     std::list<std::reference_wrapper<Type>> get(Key1 const & key);
     std::list<std::reference_wrapper<Type>> get(Key2 const & key);
     template<class ...Args> Type & put(Key1 const & key, Args const & ...args);
     template<class ...Args> Type & put(Key2 const & key, Args const & ...args);
  };

Here, I have a public interface for a class. Underlying data structures don't matter. Everything will work just fine when Key1 and Key2 are of different types. If they end up being the same type, the overloads will likely be impossible. Am I right thinking this?

If I am, is there a way to separate the overloads while keeping the signature as clean as possible?

EDIT: Here a more in depth sample

  template<class Key1, class Key2, class Type> class DualMultimapCache
  {
  public:
     std::list<std::reference_wrapper<Type>> get(Key1 const & key);
     std::list<std::reference_wrapper<Type>> get(Key2 const & key);
     template<class ...Args> Type & put(Key1 const & key, Args const & ...args);
     template<class ...Args> Type & put(Key2 const & key, Args const & ...args);

  private:
     std::unordered_multimap<Key1, std::reference_wrapper<Type>> map_Key1; 
     std::unordered_multimap<Key2, std::reference_wrapper<Type>> map_Key2;
  };

  template<class Key1, class Key2, class Type>
  std::list<std::reference_wrapper<Type>> DualMultimapCache<Key1, Key2, Type>::get(Key1 const & key)
  {
     auto its = map_Key1.equal_range(key);

     if (its.first == map.cend() && its.second == map.cend())
        throw std::out_of_range();
     else
        return { its.first, its.second };
  }

  template<class Key1, class Key2, class Type>
  std::list<std::reference_wrapper<Type>> DualMultimapCache<Key1, Key2, Type>::get(Key2 const & key)
  {
     auto its = map_Key2.equal_range(key);

     if (its.first == map.cend() && its.second == map.cend())
        throw std::out_of_range();
     else
        return { its.first, its.second };
  }
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Alexandre Parent
  • 127
  • 1
  • 10

2 Answers2

3

You can partial specialize the template for the case of the same key type, e.g.

template <typename Key, typename Type>
class DualMultimapCache<Key, Key, Type>
{
public:
   std::list<std::reference_wrapper<Type>> get(Key const & key);
   template<class ...Args> Type & put(Key const & key, Args const & ...args);
};
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Problem is both `Key` would have a different meaning and both overload slightly different code paths. – Alexandre Parent Dec 11 '17 at 14:33
  • 3
    @AlexandreParent then the functions would have to be named differently. When you have the exact same function name and signature, how is anyone going to know that they are supposed to be different, let alone the compiler? – SirGuy Dec 11 '17 at 14:38
  • Guessed so. Just wanted to see if there was better solutions. – Alexandre Parent Dec 11 '17 at 14:41
  • 1
    The partially specialized `DualMultimapCache::get` could take a second argument distinguishing the path to take. It could be an unsigned or an enum. – YSC Dec 11 '17 at 15:08
0

I think you have to use partial specialization with 2 parameters, but it is not convenient because you have to use slightly different interface. To solve this problem I suggest using SFINAE

template<typename Key1, typename Key2, typename Type,
     typename Enable = void > class DualMultimapCache
{
public:
   std::list<std::reference_wrapper<Type>> get(Key1 const & key);
   std::list<std::reference_wrapper<Type>> get(Key2 const & key);
   template<class ...Args> Type & put(Key1 const & key, Args const & ...args);
   template<class ...Args> Type & put(Key2 const & key, Args const & ...args);
};

template<typename Key1, typename Key2, typename Type > class DualMultimapCache < Key1, Key2, Type,
typename std::enable_if<std::is_same<Key1, Key2>::value>::type >
{
public:
    std::list<std::reference_wrapper<Type>> get(Key1 const & key);
    template<class ...Args> Type & put(Key1 const & key, Args const & ...args);
};

And you can use single interface with 3 template parameters but getting different specializations:

DualMultimapCache<int, double, int> t1; // DualMultimapCache template impl
DualMultimapCache<int, int, int> t2;    // DualMultimapCache template 
                                        // specialization impl
arturx64
  • 943
  • 4
  • 12