Very interesting and helpful answers, thank you.

 

I decided to see how many times i could shot myself in the foot.
Basically if i could make something that was lighter then the suggestions.

That is without std::shared_ptr and std::function.

And yet again i managed to get into a fight with the compiler

This is sandbox code so expect strangeness and inconsistencies:


#include <windows.h> // UNREFERENCED_PARAMETER
#include <assert.h>
#include <cstddef>
#include <utility>

// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
    TypeName(const TypeName&);               \
    void operator=(const TypeName&)

// default_delete - scalar
template<typename T>
struct default_delete
{
    inline void operator()(T obj) const
    {
        // empty
    }
};

// default_delete - scalar pointer
template<typename T>
struct default_delete<T*>
{
    inline void operator()(T *ptr) const
    {
        delete ptr;
    }
};

// default_delete - array
template<typename T>
struct default_delete<T[]>
{
    inline void operator()(T *ptr) const
    {
        delete [] ptr;
    }
};

template<typename T>
struct default_empty {
    inline void operator()(T) const    {
        // empty
    }
};

// scalar version
template<typename T, typename OnLeaveScopeFunc>
class scoped {
public:
    explicit scoped(T p = 0, OnLeaveScopeFunc func = default_delete<T>) : m_obj(p), m_onLeaveScope(func) {}

    ~scoped(void)
    {
        m_onLeaveScope(m_obj);
    }

    // Common to both pointer and array
    void reset(T p = 0)
    {
        if (m_obj != p)
        {
            m_onLeaveScope(m_obj);
            m_obj = p;
        }
    }

    T get(void) const
    {
        return m_obj;
    }

    bool operator==(const T p) const
    {
        return m_obj == p;
    }

    bool operator!=(const T p) const
    {
        return !(m_obj == p);
    }

    // Move constructor and assignment
    // This will enable make_scoped to work
    scoped(scoped &&right)
    {
        m_obj = right.m_obj;
        m_onLeaveScope = right.m_onLeaveScope;
        right.m_obj = 0;
    }

    scoped &operator=(scoped &&right)
    {
        scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
        return *this;
    }

    void swap(scoped &right)
    {
        std::swap(m_obj, right.m_obj);
        std::swap(m_onLeaveScope, right.m_onLeaveScope);
    }

    T release(void)
    {
        T temp = m_obj;
        m_obj = 0;
        return temp;
    }

private:
    T m_obj;
    OnLeaveScopeFunc m_onLeaveScope;

    DISALLOW_COPY_AND_ASSIGN(scoped);
};

// scalar pointer version
template<typename T, typename OnLeaveScopeFunc>
class scoped<T*, OnLeaveScopeFunc> {
public:
    typedef T element_type;

    explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(default_empty<T>) {}

    ~scoped(void)
    {
        m_onLeaveScope(m_ptr);
    }

    // Common to both pointer and array
    void reset(T *p = nullptr)
    {
        if (m_ptr != p)
        {
            m_onLeaveScope(m_ptr);
            m_ptr = p;
        }
    }

    T *get(void) const
    {
        return m_ptr;
    }

    bool operator==(const T *p) const
    {
        return m_ptr == p;
    }

    bool operator!=(const T *p) const
    {
        return !(m_ptr == p);
    }

    // Move constructor and assignment
    // This will enable make_scoped to work
    scoped(scoped &&right)
    {
        m_ptr = right.m_ptr;
        m_onLeaveScope = right.m_onLeaveScope;
        right.m_ptr = nullptr;
        right.m_onLeaveScope = m_onLeaveScope_empty;
    }

    scoped &operator=(scoped &&right)
    {
        scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
        return *this;
    }

    void swap(scoped &right)
    {
        std::swap(m_ptr, right.m_ptr);
        std::swap(m_onLeaveScope, right.m_onLeaveScope);
    }

    T *release(void)
    {
        T *temp = m_ptr;
        m_ptr = nullptr;
        return temp;
    }

    // Scalar pointer version
    T &operator*(void) const
    {
        assert(m_ptr != nullptr);
        return *m_ptr;
    }

    T *operator->(void) const
    {
        assert(m_ptr != nullptr);
        return m_ptr;
    }

private:
    T *m_ptr;
    OnLeaveScopeFunc m_onLeaveScope;
    OnLeaveScopeFunc m_onLeaveScope_empty;

    DISALLOW_COPY_AND_ASSIGN(scoped);
};

// array version
// FIXME: Any way to prevent missing [] from compiling ? Ex. scoped<int> bla(new int[])
// or to detect it based on how it is used further down the code ?
template<typename T, typename OnLeaveScopeFunc>
class scoped<T[], OnLeaveScopeFunc> {
public:
    typedef T element_type;

    explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func) {}

    ~scoped(void)
    {
        m_onLeaveScope(m_ptr);
    }

    // Common to both pointer and array
    void reset(T *p = nullptr)
    {
        if (m_ptr != p)
        {
            m_onLeaveScope(m_ptr);
            m_ptr = p;
        }
    }

    T *get(void) const
    {
        return m_ptr;
    }

    bool operator==(const T *p) const
    {
        return m_ptr == p;
    }

    bool operator!=(const T *p) const
    {
        return !(m_ptr == p);
    }

    // Move constructor and assignment
    // This will enable make_scoped to work
    scoped(scoped &&right)
    {
        m_ptr = right.m_ptr;
        m_onLeaveScope = right.m_onLeaveScope;
        right.m_ptr = nullptr;
    }

    scoped &operator=(scoped &&right)
    {
        scoped(std::move(right)).swap(*this); // Invoke move constructor and swap
        return *this;
    }

    void swap(scoped &right)
    {
        std::swap(m_ptr, right.m_ptr);
        std::swap(m_onLeaveScope, right.m_onLeaveScope);
    }

    T *release(void)
    {
        T *temp = m_ptr;
        m_ptr = nullptr;
        return temp;
    }

    // Array version
    // Should we use uintptr_t instead of ptrdiff_t ?
    T &operator[](std::ptrdiff_t i) const
    {
        assert(i >= 0);
        assert(m_ptr != nullptr);
        return m_ptr[i];
    }

private:
    T *m_ptr;
    OnLeaveScopeFunc m_onLeaveScope;

    DISALLOW_COPY_AND_ASSIGN(scoped);
};

// template argument deduction goodness
template <typename T, typename F>
inline scoped<T, F> make_scoped(T t, F f)
{
    return std::move(scoped<T, F>(t, f));
}

//----------------------------------------------------

inline void normal_f(int p) {
    _tprintf_s(_T("normal_f p: %d\n"), p);
}

struct normal_struct_f {
    inline void operator()(int p) const {
        _tprintf_s(_T("normal_struct_f p: %d\n"), p);
    }
};

inline void normal_fp(int *p) {
    _tprintf_s(_T("normal_fp *p: %d\n"), *p);
}

struct normal_struct_fp {
    inline void operator()(int *p) const {
        _tprintf_s(_T("normal_struct_fp *p: %d\n"), *p);
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    UNREFERENCED_PARAMETER(argc);
    UNREFERENCED_PARAMETER(argv);

    // 2010-02-24
    auto f = [](int p) {
        _tprintf_s(_T("f p: %d\n"), p);
    };

    auto fp = [](int *p) {
        _tprintf_s(_T("fp *p: %d\n"), *p);
    };

    {
        auto t1 = make_scoped(1975, f);
        auto t2 = make_scoped(1975, normal_f);
        auto t3 = make_scoped(1975, normal_struct_f());
    }
    _tprintf_s(_T("\n")); // For output readability
    {
        auto t1 = make_scoped(new int(1975), fp);
        auto t2 = make_scoped(new int(1975), normal_fp);
        auto t3 = make_scoped(new int(1975), normal_struct_fp());
    }
    _tprintf_s(_T("\n")); // For output readability
    return 0;
}


Ok, so the problem i'm having:
In <T*> version of 'scoped(scoped &&right)' (that's the class i've used as test subject)
How to make 'right.m_onLeaveScope' accept the new empty function ?


The compiler complains:
"error C2275: 'default_empty<T>' : illegal use of this type as an expression"
due to 'm_onLeaveScope_empty(default_empty<T>)' on line
explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(default_empty<T>) {}

I can't see why i doesn't want to work.

"OnLeaveScopeFunc func = default_delete<T>"

Worked fine so why doesn't that want to work ?

I've also tried:

"OnLeaveScopeFunc func_empty = default_empty<T>"

'm_onLeaveScope_empty(func_empty)'

 

explicit scoped(T *p = nullptr, OnLeaveScopeFunc func = default_delete<T>, OnLeaveScopeFunc func_empty = default_empty<T>) : m_ptr(p), m_onLeaveScope(func), m_onLeaveScope_empty(func_empty) {}