Template and overloads
Asked Answered
D

2

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 };
  }
Danialah answered 11/12, 2017 at 14:26 Comment(4)
Is a DualMultimapCache<T, T, Foo> allowed at all? If no, just static_assert(!std::is_same<Key1, Key2>::value, "").Cryostat
Use different names for get(/put) ? get_from_key1/get_from_key2 ?Encroach
@Cryostat It is in the case I'm working with... In fact, it may well be the standard use case.Danialah
@Encroach That's the solution I was thinking, but it'd feel weird to not complete the same interface my other classes have.Danialah
E
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);
};
Ethylene answered 11/12, 2017 at 14:32 Comment(4)
Problem is both Key would have a different meaning and both overload slightly different code paths.Danialah
@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?Eyre
Guessed so. Just wanted to see if there was better solutions.Danialah
The partially specialized DualMultimapCache<Key, Key, Type>::get could take a second argument distinguishing the path to take. It could be an unsigned or an enum.Tick
D
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
Dewan answered 11/12, 2017 at 15:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.