2016-02-02 3 views
3

Рассмотрим следующий класс, который оборачивает контейнер и типа стирания 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.

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

+0

Пожалуйста, смоделируйте данные правильно (ваши вложенные классы выглядят как вложенные пространства имен) –

+0

Я хотел бы помочь вам, но я бы сказал, что понял, что не так с моим фрагментом. Прости. Он хорошо отформатирован, так что в чем проблема? Я забыл ';', это исправлено. – skypjack

+0

Нам не хватает связи между 'operator ==' и классом, который обертывает контейнер и ... ". 'class C' не имеет контейнеров. – juanchopanza

ответ

0

Несмотря на хорошее предложение от 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); 
} 

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

+0

@juanchopanza Я ожидаю хотя бы ваш комментарий к этому фрагменту! :-) – skypjack

-2

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

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()); } 
}; 
+0

Проблема заключается в том, что 'static_cast' может привести к неопределенному поведению, если базовая модель не является одинаковой для обоих контейнеров, поэтому для разведения результирующего экземпляра может привести к сбою. Более того, приведение к 'Модели' в том виде, как оно, приводит, вероятно, к ошибке компиляции. – skypjack

+0

Нет, не может. Проверка typeid перед этим гарантирует, что static_cast определяется поведением. Я также не получаю ошибку компиляции с clang. Учитывая, что модель объявлена ​​окончательной, вы также можете использовать dynamic_cast. – Nevin

+0

пропустил его, вы правы, спасибо – skypjack