为什么这个C ++ STL分配器不分配?

时间:2009-02-12 01:25:18

标签: c++ stl allocation

我正在尝试编写一个派生自std::allocator的自定义STL分配器,但不知怎的,所有对allocate()的调用都转到基类。我把它缩小到这段代码:

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return 0;
    }
};

int main()
{
    vector<int, a<int>> v(1000, 42);
    return 0;
}

我期待“哟!”打印,然后是一些可怕的错误,因为我实际上没有分配任何东西。相反,该程序运行正常,并没有打印。我做错了什么?

我在gcc和VS2008中得到了相同的结果。

4 个答案:

答案 0 :(得分:6)

您需要提供重新绑定成员模板以及C ++标准中分配器要求中列出的其他内容。例如,您需要一个模板复制构造函数,它不仅接受allocator<T>,还接受allocator<U>。例如,一个代码可能会执行,例如std :: list可能会执行

template<typename Allocator>
void alloc1chunk(Allocator const& alloc) {
    typename Allocator::template rebind<
        wrapper<typename Allocator::value_type>
      >::other ot(alloc);
    // ...
}

如果没有正确的重新绑定模板,或者没有相应的复制构造函数,代码将失败。猜测要求是什么,你将无处可用。迟早你将不得不处理依赖于那些分配器要求的一部分的代码,并且代码将因为你的分配器违反它们而失败。我建议您在20.1.5中的一些工作草稿中查看一下您的标准副本。

答案 1 :(得分:4)

在这种情况下,问题是我没有覆盖分配器的重新绑定成员。此版本有效(在VS2008中):

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return 0;
    }

    template <typename U> struct rebind
    {
        typedef a<U> other;
    };
};

int main() {
    vector<int, a<int>> v(1000, 42);
    return 0;
}

我是通过调试STL标题找到的。

这是否有效将完全取决于STL实现,所以我认为最终,Klaim是正确的,因为这不应该这样做。

答案 2 :(得分:2)

我有两个用于创建自定义分配器的模板;如果在自定义类型上使用它,则第一个自动工作:

template<>
class std::allocator<MY_TYPE>
{
public:
    typedef size_t      size_type;
    typedef ptrdiff_t   difference_type;
    typedef MY_TYPE*    pointer;
    typedef const MY_TYPE*  const_pointer;
    typedef MY_TYPE&    reference;
    typedef const MY_TYPE&  const_reference;
    typedef MY_TYPE     value_type;

    template <class U>
    struct rebind
    {
        typedef std::allocator<U> other;
    };

    pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
    {
        return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
    }
    void construct(pointer p, const_reference val)
    {
        ::new(p) T(val);
    }
    void destroy(pointer p)
    {
        p->~T();
    }
    void deallocate(pointer p, size_type n)
    {
        FREE_FUNC(p);
    }
    size_type max_size() const throw()
    {
        // return ~size_type(0); -- Error, fixed according to Constantin's comment
        return std::numeric_limits<size_t>::max()/sizeof(MY_TYPE);
    }
};

当我们希望将自己的分配器用于具有标准分配器的预定义类型时使用第二个,例如char,wchar_t,std :: string等:

    namespace MY_NAMESPACE
    {

    template <class T> class allocator;

    // specialize for void:
    template <>
    class allocator<void>
    {
    public:
        typedef void*       pointer;
        typedef const void* const_pointer;
        // reference to void members are impossible.
        typedef void        value_type;

        template <class U>
        struct rebind
        {
            typedef allocator<U> other;
        };
    };

    template <class T>
    class allocator
    {
    public:
        typedef size_t      size_type;
        typedef ptrdiff_t   difference_type;
        typedef T*      pointer;
        typedef const T*    const_pointer;
        typedef T&      reference;
        typedef const T&    const_reference;
        typedef T       value_type;

        template <class U>
        struct rebind
        {
            typedef allocator<U> other;
        };

        allocator() throw()
        {
        }
        template <class U>
        allocator(const allocator<U>& u) throw()
        {
        }
        ~allocator() throw()
        {
        }

        pointer address(reference r) const
        {
            return &r;
        }
        const_pointer address(const_reference r) const
        {
            return &r;
        }
        size_type max_size() const throw()
        {
            // return ~size_type(0); -- Error, fixed according to Constantin's comment
            return std::numeric_limits<size_t>::max()/sizeof(T);
        }
        pointer allocate(size_type n, allocator<void>::const_pointer hint = 0)
        {
            return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
        }
        void deallocate(pointer p, size_type n)
        {
            FREE_FUNC(p);
        }

        void construct(pointer p, const_reference val)
        {
            ::new(p) T(val);
        }
        void destroy(pointer p)
        {
            p->~T();
        }
    };

template <class T1, class T2>
inline
bool operator==(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
    return true;
}

template <class T1, class T2>
inline
bool operator!=(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
    return false;
}

}

上面的第一个模板,对于您自己定义的类型,不需要任何进一步处理,但是由标准容器类自动使用。当在标准类型上使用时,第二个模板需要进一步的工作。例如,对于std :: string,在声明该类型的变量时,必须使用以下构造(对于typedef来说最简单):

std::basic_string<char>, std::char_traits<char>, MY_NAMESPACE::allocator<char> >

答案 3 :(得分:1)

以下代码按预期打印“哟” - 您所看到的是我们的老朋友“未定义的行为”。

#include <iostream>
#include <vector>
using namespace std;

template <typename T> class a : public std::allocator<T> {
public:
    T* allocate(size_t n, const void* hint = 0) const {
        cout << "yo!";
        return new T[10000];
    }
};

int main()
{
    vector<int, a<int> > v(1000, 42);
    return 0;
}

编辑:我刚刚查看了关于默认分配器的C ++标准。没有禁止继承它。事实上,据我所知,标准的任何部分都没有这样的禁令。