доступ к перегрузке операторов класса, который упакован в std :: shared_ptr - PullRequest
2 голосов
/ 22 сентября 2011

идея состоит в том, что я хочу, чтобы класс, который был обернут в std::shared_ptr, все еще мог использоваться так же, как они не были указателем, например, оператор =, который был определен в моем классе, все еще может использоваться после моего классаобернуто std::shared_ptr.

например

template <class Ty> class shared_ptr_proxy : public std::shared_ptr<Ty> {
public:
    template<class Other> shared_ptr_proxy& operator=(const Other& rhs)
    {
        (*this->get()) = rhs;
        return *this;
    }
    template<class Other> explicit shared_ptr_proxy(Other * ptr) 
        : std::shared_ptr<Ty>(ptr){};
};

// usage :
shared_ptr_proxy<float> obj = shared_ptr_proxy<float>(new float);
obj = 3.14;

его работа, но есть ли способ, которым мне не нужно создавать shared_ptr_proxy или наследовать класс от std::shared_ptr?

и

если я так поступлю, есть ли оговорка, о которой я должен позаботиться?

Ответы [ 5 ]

3 голосов
/ 22 сентября 2011

Это зависит от того, для чего вы хотите прокси. Полный прокси-сервер может выглядеть так, как будто у вас есть значение, поэтому вы должны предоставить операторы преобразования.

В таком случае, возможно, было бы неуместно наследовать от shared_ptr, потому что вы можете наследовать функции, которые вместо этого вы хотите полагаться на неявные преобразования.

Сравните порядок сортировки товаров:

#include <memory>
#include <vector>
#include <algorithm>
#include <iostream>

template <class Ty> class shared_ptr_proxy   {
    std::shared_ptr<Ty> ptr;
public:
    template<class Other> explicit shared_ptr_proxy(Other * p) 
        : ptr(std::shared_ptr<Ty>(p)){};

    template<class Other> shared_ptr_proxy& operator=(const Other& other)
    {
        *ptr = other;
        return *this;
    }

    operator Ty& () { return *ptr; }
    operator const Ty& () const { return *ptr; }
};

int main()
{
    std::vector<shared_ptr_proxy<int> > vec {
        shared_ptr_proxy<int>(new int(10)), 
        shared_ptr_proxy<int>(new int(11)), 
        shared_ptr_proxy<int>(new int(9))
    };
    vec.back() = 8;  //use assignment
    std::sort(vec.begin(), vec.end());  //sort based on integer (not pointer) comparison
    for (unsigned i = 0; i != vec.size(); ++i) {
        std::cout << vec[i] << ' ';  //output stored values
    }
}

#include <memory>
#include <vector>
#include <algorithm>
#include <iostream>

template <class Ty> class shared_ptr_proxy : public std::shared_ptr<Ty>   {
public:
    template<class Other> explicit shared_ptr_proxy(Other * p) 
        : std::shared_ptr<Ty>(p){};

    template<class Other> shared_ptr_proxy& operator=(const Other& other)
    {
        *this->get()= other;
        return *this;
    }

    operator Ty& () { return *this->get(); }
    operator const Ty& () const { return *this->get(); }
};

int main()
{
    std::vector<shared_ptr_proxy<int> > vec {
        shared_ptr_proxy<int>(new int(10)), 
        shared_ptr_proxy<int>(new int(11)), 
        shared_ptr_proxy<int>(new int(9))
    };
    vec.back() = 8;  //the only thing that works
    std::sort(vec.begin(), vec.end());  //sort based on pointer values
    for (unsigned i = 0; i != vec.size(); ++i) {
        std::cout << vec[i] << ' ';  //outputs addresses
    }
}
1 голос
/ 22 сентября 2011

Нет, вы не можете сделать это прозрачным образом, и, возможно, это будет весьма запутанным, если вы сможете.

1 голос
/ 22 сентября 2011

operator= должен быть членом класса, который вы перегружаете. Так что нет, вы не можете сделать это ненавязчиво.

0 голосов
/ 23 сентября 2011

Извините, я не думаю, что вы можете обойтись без наследования или пользовательской оболочки, вы не можете перегрузить operator = вне определения shared_ptr, и наследование в этом случае не рекомендуется из-за характера shared_ptr. Однако, если вы напишите пользовательскую оболочку, вы можете сделать ее достаточно универсальной, чтобы она работала для каждого типа.

Это возможно только в C ++ 11, и даже там это сложно. Вам нужно decltype и std::declval для определения типа возврата оператора, а также ссылки на rvalue и std::forward для идеальной передачи параметров. См. этот вопрос и его ответы для примеров.

И, как уже упоминалось в этом вопросе, у меня реализован класс-оболочка для указателя: http://frigocoder.dyndns.org/svn/Frigo/Lang/ref

Однако он имеет некоторые отличия от того, что вы хотите:

  • Оба operator = (ref&) и operator = (ref&&) копируют только указатели. Однако из-за этого и конструктора прокси-копии неявное operator = (const T&) делает конструкцию копии и копию указателя вместо назначения или получения адреса. Это мой сознательный выбор, назначение может создать проблемы, если объект используется совместно, а получение указателя от объекта, выделенного из стека, небезопасно.

  • Принятие возвращаемого типа составных операторов присваивания, таких как operator +=, не работает из-за невыполненной функции GCC. Это не проблема, поскольку они интерпретируются заново: x += y вызывает x = (x + y), выполняет конструкцию копирования / перемещения и копирование указателя. Это также мой сознательный выбор - оставить общий объект без изменений.

  • Мусор, собранный с помощью Boehm GC вместо подсчета ссылок. Это также мой сознательный выбор, подсчет ссылок - довольно плохая альтернатива уборке мусора.

0 голосов
/ 22 сентября 2011

Разыменование общего указателя:

std::shared_ptr<float> obj(new float);
*obj = 3.14;
...