пользовательские умные указатели и полиномиальное число операторов сравнения - PullRequest
3 голосов
/ 02 января 2012

Допустим, я хочу реализовать умный указатель a_ptr, который можно сравнивать с другими умными указателями.

Затем мне нужно реализовать все перестановки операторов сравнения:

template<class T, class U>
bool operator==(const a_ptr<T>& a, const a_ptr<U>& b)
{
    return a.get() == b.get();
}

template<class T, class U>
bool operator==(const std::shared_ptr<T>& a, const a_ptr<U>& b)
{
    return a.get() == b.get();
}

template<class T, class U>
bool operator==(const a_ptr<T>& a, const std::shared_ptr<U>& b)
{
    return a.get() == b.get();
} 

и т. д. ... для остальных операторов.

Тогда, возможно, я бы хотел реализовать еще один умный указатель b_ptr, который дал бы мне 6 версий для каждого оператора сравнения (поскольку я хочу, чтобы он такжеработать с a_ptr), явно не поддается управлению.

Есть ли способ обойти эту проблему?

РЕДАКТИРОВАТЬ:

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

template<typename T>
class a_ptr
{
public:
    const T* get() const {return p_.get();}
private:
    std::shared_ptr<T> p_;
};

template<typename T>
class b_ptr
{
public:
    const T* get() const {return p_.get();}
private:
    a_ptr<T> p_;
};

Ответы [ 6 ]

5 голосов
/ 02 января 2012

Если какое-либо из них, кроме того, которое сравнивает два a_ptr, когда-либо остается верным, в вашей программе есть ошибка.Так что просто брось это.Умный указатель умный, потому что он отвечает за управление памятью позади него.И вы просто не можете иметь два разных интеллектуальных указателя, управляющих одним фрагментом памяти.

Рассмотрим unique_ptr и shared_ptr.Один уничтожает указатель, как только уничтожающий умный указатель уничтожается.Второй уничтожает указатель только тогда, когда все владеющие умными указателями уничтожаются.Я думаю, совершенно очевидно, что это быстро приведет к двойному удалению и другим забавным вещам.

4 голосов
/ 02 января 2012

Бен Фойгт на правильном пути - за исключением, конечно, у нас нет концепций.

template<
    typename Lhs
    , typename Rhs
    , typename = typename std::enable_if<
        /* magic */
    >::type
>
bool
operator==(Lhs const& lhs, Rhs const& rhs)
{
    return lhs.get() == rhs.get();
}

Это должно быть в одном и том же пространстве имен a_ptr и b_ptr для того, чтобы ADL пнул (поэтому, если они находятся в отдельных пространствах имен, вам нужна одна версия каждого оператора для каждого пространства имен).

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

template<typename T>
struct is_smart_pointer: std::false_type {};

template<typename T, typename D>
struct is_smart_pointer<std::unique_ptr<T, D>>: std::true_type {};
// and so on...

Более сложная черта будет проверять, поддерживает ли тип член get. На самом деле нам не нужна эта черта!

template<typename Lhs, typename Rhs>
auto operator==(Lhs const& lhs, Rhs const& rhs)
-> decltype( lhs.get() == rhs.get() )
{
    return lhs.get() == rhs.get();
}

Этот простой оператор будет выбран ADL, за исключением того, что он сам выдаст SFINAE, если один из типов не поддерживает get, или если сравнение не работает.

3 голосов
/ 02 января 2012

Вы можете просто пометить ваши типы интеллектуальных указателей, а затем реализовать универсальную версию, которая опирается как минимум на один из переданных аргументов, которые будут помечены:

template <typename T> struct is_my_smartpointer: std::false_type {};
template <typename T> struct is_my_smartpointer<a_ptr<T> >: std::true_type {};

template <typename S, typename T>
typename std::enable_if<is_my_smartpointer<S>::value || is_my_smartpointer<T>::value, bool>::type
operator== (S const& s, T const& t) {
    return s.get() == t.get();
}
3 голосов
/ 02 января 2012

Как насчет:

template<typename T> struct you_need_a_pointer_to_do_that{} unwrap_ptr(const T&);

template<typename U> U* unwrap_ptr(U* p) { return p; }

template<typename U> U* unwrap_ptr(const a_ptr<U>& p) { return p.get(); }

template<typename U> U* unwrap_ptr(const unique_ptr<U>& p) { return p.get(); }

...

template<typename T, typename PU> auto operator<(const a_ptr<T>& a, const PU& b) -> decltype(unwrap_ptr(a) < unwrap_ptr(b)) { return unwrap_ptr(a) < unwrap_ptr(b); }

Бит decltype отклоняет не указатели из-за SFINAE. А помощник unwrap_ptr гомогенизирует синтаксис для доступа к указателю, причем сложность только линейна по числу типов указателей.

0 голосов
/ 02 января 2012

Это может быть так же сложно, как написать черту sfinae для умных указателей.Как только вы его получите, решение довольно простое, вам не нужен c ++ 11 для этого:

#include <memory>
#include <type_traits>
#include <iostream>

namespace my {
template<class T>
class a_ptr
{
   T* t_;
public:
   typedef T element_type;
   element_type* get() const throw() { return t_; }
};

template<class T> class is_smart_ptr; // sfinae test for T::get()?

template<class T, class U>
typename std::enable_if<
   is_smart_ptr<T>::value &&
   is_smart_ptr<U>::value,
   bool
>::type
operator==(const T& a, const U& b)
{
    return a.get() == b.get();
}
}

int main()
{
   my::a_ptr<int> a;
   std::shared_ptr<int> b;
   a == b;
}

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

0 голосов
/ 02 января 2012

Вы можете унаследовать свой умный указатель от существующего умного указателя.Таким образом, вам вообще не придется реализовывать никаких операторов.
Также, как сказал Cat Plus Plus.Вы не можете иметь два интеллектуальных указателя, управляющих одним фрагментом памяти.Так что, если вы реализуете умный указатель, вы должны убедиться, что он верен.Вы должны сделать operator= приватным, чтобы его нельзя было скопировать в другое место.

...