简体   繁体   中英

Templated operator ==

I have what is essentially class containing a std::map where the values are shared_ptrs wrapping a container which holds different types. Skeleton code follows:

// Just a basic example class
class MyClass {
  public:
    explicit MyClass(int i) : mI(i) {}
    bool operator==(const MyClass& rhs) { return mI == rhs.mI; }
  private:
    int mI;
};

// A class into which key value pairs can be added where the value
// can be of a different type.
class MultipleTypeMap {

 public:

   template <typename T>
   void AddObject(const std::string& key, const T object) {
     auto ptr = make_shared<B<MyClass>>(std::move(object));
     mSharedPtrMap.insert(pair<string, shared_ptr<A>>("key", ptr));
   }
   // ...

 private:

    class A {
    public:
      virtual ~A() = default;
    };

    template<typename T>
    class B : public A {
    public:
      explicit B(const T& t) : item(t) {}
      const T item;
    };

    map<string, shared_ptr<A>> mSharedPtrMap;
};

int main() {

    MyClass m(1);
    MultipleTypeMap multiMap;
    multiMap.AddObject("test", m);

    MyClass n(1);
    MultipleTypeMap multiMap2;
    multiMap2.AddObject("test", n);

    if (multiMap == multiMap2) {
        cout << "Equal" << endl;
    }

    return 0;
}

How should a generic == operator of MultipleTypeMap be written so that it compares the contents of mSharedPtrMap by checking that both the lhs and rhs objects have the same number of keys, the same keys and the same objects where same means that the == operator of the keys / objects evaluates to true?

If you type erase (and later on don't know which type you previously stored), then all the functionality must be provided by the base class interface. So, we need a virtual operator== in A that is implemented in each B .

Here is an implementation:

class MultipleTypeMap {

 public:
   template <typename T>
   void AddObject(const std::string& key, T object) {
     auto ptr = std::make_unique<B<T>>(std::move(object));
     mMap.emplace(key, std::move(ptr));
   }
   // ...

    bool operator==(const MultipleTypeMap& other) const
    {
        // Sizes must be equal.
        if (mMap.size() != other.mMap.size())
            return false;

        // Sizes are equal, check keys and values in order.
        auto itOther = other.mMap.begin();
        for (auto it = mMap.begin(); it != mMap.end(); ++it, ++itOther)
        {
            if (it->first != itOther->first)
                return false;
            if (*it->second != *itOther->second)
                return false;
        }
        // No differences found.
        return true;
    }
    bool operator!=(const MultipleTypeMap& rhs) const { return !(*this == rhs); }

 private:

    class A {
    public:
      virtual ~A() = default;

      virtual bool operator==(const A& other) const = 0;
      bool operator!=(const A& other) const { return !(*this == other); }
    };

    template<typename T>
    class B : public A
    {
    public:
        explicit B(const T& t) : item(t) {}

        bool operator==(const A& other) const override
        {
            const B<T>* otherB = dynamic_cast<const B<T>*>(&other);
            // If the cast fails, types are different.
            if (!otherB)
                return false;
            // Note: The above is probably slow, consider storing (on construction)
            // and checking typeids instead.

            // Check item equality.
            return item == otherB->item;
        }

        const T item;
    };

    std::map<std::string, std::unique_ptr<A>> mMap;
};

Demo with tests

Note: I didn't fix every inconsistency in the original code. (Do you want to move or copy-construct your T ? Why store const objects when your MyClass comparison operator is not const ?)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM