Как реализовать умный указатель, который может быть создан с помощью void? - PullRequest
4 голосов
/ 21 августа 2009

Некоторые шаблоны интеллектуальных указателей, такие как boost :: shared_ptr, могут быть созданы с помощью void для хранения произвольного объекта:

http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/sp_techniques.html#pvoid

Ниже приведена минимальная реализация scoped_ptr. Когда создается экземпляр void, компилятор жалуется на то, что в операторе разыменования была сформирована недопустимая «ссылка на void». Похоже, что правило «ошибка замены не является ошибкой» (SFINAE) не охватывает эту ситуацию.

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

#include <cstdlib>

template<typename T>
void destroy(T* ptr)
{
    delete ptr;
}

class scoped_ptr_impl_base
{
public:
    virtual ~scoped_ptr_impl_base() { }
};

template<typename T, typename F>
class scoped_ptr_impl : public scoped_ptr_impl_base
{
public:
    scoped_ptr_impl(T* ptr, F dtor)
        : m_ptr(ptr), m_dtor(dtor)
    {
    }

    virtual ~scoped_ptr_impl()
    {
        m_dtor(m_ptr);
    }

private:
    T* m_ptr;
    F m_dtor;
};

template<typename T>
class scoped_ptr
{
public:
    explicit scoped_ptr(T* ptr = 0)
        : m_ptr(ptr),
          m_impl(new scoped_ptr_impl<T, void (*)(T*)>(&destroy<T>))
    {
    }

    template<typename F>
    scoped_ptr(T* ptr, F dtor)
        : m_ptr(ptr),
          m_impl(new scoped_ptr_impl<T, F>(ptr, dtor))
    {
    }

    ~scoped_ptr()
    {
        delete m_impl;
    }

    T& operator*()
    {
        return *m_ptr;
    }

    T* operator->()
    {
        return m_ptr;
    }

private:
    T* m_ptr;
    scoped_ptr_impl_base* m_impl;

    scoped_ptr(const scoped_ptr&);
    scoped_ptr& operator=(const scoped_ptr&);
};

int main()
{
    scoped_ptr<void> p(std::malloc(1), std::free);
    // scoped_ptr.cpp: In instantiation of `scoped_ptr<void>':
    // scoped_ptr.cpp:76:   instantiated from here
    // scoped_ptr.cpp:56: error: forming reference to void
    // (g++ 4.3.3)

    return 0;
}

1 Ответ

8 голосов
/ 21 августа 2009

Вы можете использовать черту типа для ссылочного типа:

template<typename T>
struct type_trait
{
    typedef T& reference;
};

template<>
struct type_trait<void>
{
    typedef void reference;
};

тогда в вашем scoped_ptr_impl:

typename type_trait<T>::reference operator*()
{
    return *m_ptr;
}

Не уверен, что void - правильный тип в специализации. Какой тип вы хотите вернуть?

...