【问题标题】:Why doesn't this C++ STL allocator allocate?为什么这个 C++ STL 分配器不分配?
【发布时间】:2010-10-07 01:55:11
【问题描述】:

我正在尝试编写一个从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 中得到相同的结果。

【问题讨论】:

标签: c++ stl allocation


【解决方案1】:

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

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> >

【讨论】:

  • 看起来max_size() 应该返回std::numeric_limits&lt;size_t&gt;::max() / sizeof(MY_TYPE)
  • @Constantin:很好的观察,非常感谢!我将相应地更新代码。 :-)
【解决方案2】:

您将需要提供重新绑定成员模板和 C++ 标准的分配器要求中列出的其他内容。例如,您需要一个模板复制构造函数,它不仅接受allocator&lt;T&gt;,还接受allocator&lt;U&gt;。例如,一个代码可能会这样做,例如 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 中的标准副本的一些工作草案中查看它们。

【讨论】:

  • 关于阅读实际接口要求的要点是——否则你永远不会确定知道你正在处理分配器应该做的所有事情。
【解决方案3】:

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

#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++ 标准。没有禁止继承它。事实上,据我所知,该标准的任何部分都没有这样的禁令。

【讨论】:

  • 你测试过这段代码吗?它不能工作,因为它与问题基本相同。我也只是在调试和发布模式下运行它,它不起作用。
  • 使用 i686-apple-darwin8-g++-4.0.1 对我来说很好
  • 是的,我确实测试过它 g+++ 3.4.5 - zab
  • 因此,鉴于这一点以及 marvinalone 发现他需要实现 rebind,上述代码是否有效似乎取决于 vector 的实现是否在内部使用了 rebind。跨度>
【解决方案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 是正确的,不应该以这种方式进行。

【讨论】:

  • 实际上虚函数调度不用于分配器调用,因为在编译时类型是已知的,所以我相信这现在应该总是有效的。不打扰从 std::allocator 继承的唯一原因是因为它实际上并没有为您节省很多打字!
  • ...但请参阅 litb 对您需要实现的另一个要求(模板复制 ctor)的回答。
猜你喜欢
  • 2021-11-12
  • 2010-09-07
  • 2021-10-06
  • 2014-10-23
  • 1970-01-01
  • 2014-10-25
  • 2016-10-08
  • 2012-09-23
  • 2018-05-27
相关资源
最近更新 更多