оператор= = типа стертого контейнера


Рассмотрим следующий класс, который обертывает контейнер и type-erase s его тип:

class C final {
    struct B {
        virtual bool empty() const noexcept = 0;
    };

    template<class T, class A>
    struct D: public B {
        // several constructors aimed to
        // correctly initialize the underlying container

        bool empty() const noexcept override { return v.empty(); }

    private:
        std::vector<T, A> v;
    };

    // ...

public:
    //...

    bool operator==(const C &other) const noexcept {
        // ??
        // would like to compare the underlying
        // container of other.b with the one
        // of this->b
    }

private:
    // initialized somehow
    B *b;
};

Я хотел бы добавить operator== в класс C.
Внутренне он должен просто вызвать тот же оператор для нижележащих контейнеров, но я застрял на этой проблеме, потому что не знаю, как это сделать.
Идея заключается в том, что два экземпляра C равны, если operator== их нижележащих контейнеров возвращают true.

Что бы я ни пробовал до сих пор, я всегда заканчивал будучи неспособным получить тип одного из двух нижележащих контейнеров, главным образом один из другого.
Есть ли простое решение, которое я не вижу в данный момент, или я должен сдаться?

2 3

2 ответа:

Несмотря на хорошее предложение от juanchopanza, я обнаружил, что, насколько глубинные контейнеры представляют одно понятие (например, разных специализаций вектора), может быть, нет необходимости тип-сотриД итератора.

Ниже приведена возможная реализация, которая опирается на operator[] и size Метод-член:

#include <vector>
#include <cassert>

class Clazz final {
    struct BaseContainer {
        virtual std::size_t size() const noexcept = 0;
        virtual int operator[](std::size_t) const = 0;
        virtual void push_back(int) = 0;
    };

    template<class Allocator>
    struct Container: public BaseContainer {
        Container(Allocator alloc): v{alloc} { }
        std::size_t size() const noexcept override { return v.size(); }
        int operator[](std::size_t pos) const override { return v[pos]; }
        void push_back(int e) override { v.push_back(e); }

    private:
        std::vector<int, Allocator> v;
    };

public:
    template<class Allocator = std::allocator<int>>
    Clazz(const Allocator &alloc = Allocator{})
        : container{new Container<Allocator>{alloc}} { }

   ~Clazz() { delete container; }

    void push_back(int e) { container->push_back(e); }

    bool operator==(const Clazz &other) const noexcept {
        const BaseContainer &cont = *container;
        const BaseContainer &oCont = *(other.container);
        bool ret = (cont.size() == oCont.size());

        for(std::vector<int>::size_type i = 0, s = cont.size(); i < s && ret; i++) {
            ret = (cont[i] == oCont[i]);
        }

        return ret;
    }

    bool operator!=(const Clazz &other) const noexcept {
        return !(*this == other);
    }

private:
    BaseContainer *container;
};

int main() {
    Clazz c1{}, c2{}, c3{};
    c1.push_back(42);
    c2.push_back(42);
    assert(c1 == c2);
    assert(c1 != c3);
}

Открыт для критики, надеясь, что этот ответ поможет другим пользователям. :- )

Предполагая, что вы хотите вернуть false при сравнении двух разных контейнеров, это должно сделать работу (осторожно: непроверено):

class Container
{
    struct Concept
    {
        virtual ~Concept() = default;
        virtual Concept* clone() const = 0;

        virtual bool equals(Concept const*) const = 0;
    };

    template<typename T>
    struct Model final : Concept
    {
        Model(T t) : data{std::move(t)} {}
        Model* clone() const override { return new Model{*this}; }

        virtual bool equals(Concept const* rhs) const override
        {
            if (typeid(*this) != typeid(*rhs))
                return false;

            return data == static_cast<Model const*>(rhs)->data;
        }

        T data;
    };

    std::unique_ptr<Concept> object;

public:
    template<typename T>
    Container(T t) : object(new Model<T>{std::move(t)}) {}

    Container(Container const& that) : object{that.object->clone()} {}
    Container(Container&& that) = default;

    Container& operator=(Container that)
    { object = std::move(that.object); return *this; }

    friend bool operator==(Container const& lhs, Container const& rhs)
    { return lhs.object->equals(rhs.object.get()); }
};