2009-02-12 121 views
12

我试图写一个自std::allocator派生的自定义STL分配器,但不知何故所有调用allocate()都转到基类。我已经缩小到这个代码:为什么这个C++ STL分配器没有分配?

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中得到了相同的结果。

+0

相关,请参阅[为什么不从的std ::分配器继承](HTTP://计算器。COM/Q/21081796)。 – jww 2016-08-13 01:53:17

回答

6

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

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

,如果有任何不存在正确的重新绑定模板存在没有相应的拷贝构造函数的代码会失败,或。在猜测需求是什么时,你将毫无用处。迟早你将不得不处理依赖于这些分配器需求的一部分的代码,并且代码将因为分配器违反它们而失败。我建议你在一些工作草案中看看你的标准副本20.1.5

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

+0

这对我不起作用。 VS 2008. – 2009-02-12 01:42:07

+0

你测试了这段代码吗?它不能工作,因为它基本上与问题相同。我只是在调试和发布模式下运行它,它不起作用。 – Klaim 2009-02-12 01:43:10

+0

适用于i686-apple-darwin8-g ++ - 4.0.1 – 2009-02-12 01:58:29

4

在这种情况下,问题是我没有覆盖分配器的rebind成员。此版本的工作原理(在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

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

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); 
    } 
}; 

二是使用时,我们希望有我们自己的预定义类型与标准分配器分配,例如字符,wchar_t的,性病::串等:以上

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