Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to NOT overload the std::map::at member function in the case where the two template types are the same?

Tags:

c++

c++11

I have a here a bidirectional map. I make it form 0. I defined member functions like insert count size and others and of course a function at which returns a reference to the mapped value of the element identified with the given key. Until type A is not the same with type B everything works fine, but when type A is the same with type B i get an error that i try to overload this function at, which is correct :( But my mind can`t help me with a method to solve this error. Maybe you can give me an example or tell me what i can do in this case :)

template <class A,class B>

class BidirectionalMap
{
public:
    void insert(A a,B b)
    {
        m1.insert(std::pair<A,B> (a,b));
        m2.insert(std::pair<B,A> (b,a));
    }
    BidirectionalMap& operator =(BidirectionalMap &a)
    {
        m1=a.m1;
        m2=a.m2;
        return *this;
    }
    A& at(const A& a)
    {
        if(m1.find(a)!=m1.end()) return m1.at(a);
        else return m2.at(a);
    }
    const B& at(const A& b) const
    {
        return m1.at(b);
    }
    const A& at(const B& a) const
    {
        return m2.at(a);
    }
    int size() const
    {
        return m1.size();
    }
    int count(const A& a) const
    {
        return m1.count(a);
    }
    int count(const B& b) const
    {
        return m2.count(b);
    }
    B& operator[](const A& a)
    {
        return m1[a];
    }
    A& operator[](const B& b)
    {
        return m2[b];
    }
private:
    std::map<A,B> m1;
    std::map<B,A> m2;
};

And what if i cannot modify this sequence in main()?

  BidirectionalMap<int, int> f;
  f.insert(3, 18);
  f.insert(8, 2);
  f.insert(7, 5);
  f.insert(9, 1);
  const BidirectionalMap<int, int> cf = f;

  if( f.at(5) == 7 &&
      f.count(12) == 0 &&
      f.at(8) == 2)
  {
    yourMark = cf[18] + cf[9];
  }
like image 321
Iero Avatar asked Oct 16 '25 17:10

Iero


2 Answers

Implement at in a CRTP base class.

template<class D, class A, class B>
struct crtp_at {
  D* self() { return static_cast<D*>(this); }
  D const* self() const { return static_cast<D const*>(this); }
  const B& at(const A& b) const {
    return self()->m1.at(b);
  }
  const A& at(const B& a) const {
    return self()->m2.at(a);
  }
  B& at(const A& b) {
    return self()->m1.at(b);
  }
  A& at(const B& a) {
    return self()->m2.at(a);
  }
};
template<class D, class A>
struct crtp_at<D,A,A> {
  D* self() { return static_cast<D*>(this); }
  D const* self() const { return static_cast<D const*>(this); }
  A& at(const A& a) {
    if(self()->m1.find(a)!=self()->m1.end()) return self()->m1.at(a);
    else return self()->m2.at(a);
  }
  A const& at(const A& a) const {
    if(self()->m1.find(a)!=self()->m1.end()) return self()->m1.at(a);
    else return self()->m2.at(a);
  }
};

Then your class uses the above like:

template <class A,class B>
class BidirectionalMap:public crtp_at< BiDirectionalMap<A,B>, A, B >
{
  // rest of your code
};

however, I would advise actually blocking at in that case, and any other method where it is not clear which way you are going.

You should have methods that clearly go one way or the other in your code for cases like short <-> double anyhow.

like image 57
Yakk - Adam Nevraumont Avatar answered Oct 19 '25 07:10

Yakk - Adam Nevraumont


Here is a solution which is close to @Yakk's one but is based on my old solution to the similar problem:

#include <type_traits>

template <class A, class B>
class BidirectionalMap;

template <class A, class B, bool = std::is_same<A, B>::value>
class BaseMap
{
public:

    BidirectionalMap<A, B>* self()
    {
        return static_cast<BidirectionalMap<A, B>*>(this);
    }

    const BidirectionalMap<A, B>* self() const
    {
        return static_cast<const BidirectionalMap<A, B>*>(this);
    }

    const A& at(const B& a)
    {
        return self()->m2.at(a);
    }

    const A& at(const B& a) const
    {
        return self()->m2.at(a);
    }

    // ...

};

template <class A, class B>
struct BaseMap<A, B, true> {};

template <class A, class B>
class BidirectionalMap : public BaseMap<A, B>
{
public:

    friend struct BaseMap<A, B>;

    const B& at(const A& b)
    {
        return m1.at(b);
    }

    const B& at(const A& b) const
    {
        return m1.at(b);
    }

    // ...

private:

    std::map<A, B> m1;
    std::map<B, A> m2;

};
like image 28
Constructor Avatar answered Oct 19 '25 08:10

Constructor



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!