2009-02-12 120 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> >