2012-03-13 74 views
4

我想設計一個類模板,它採用分配器類型(如標準部分17.6.3.5中定義的)作爲模板參數。我看到std::allocator_traits<A>如何用缺省設置幫助填充缺少的A成員。除此之外,標準庫中還有什麼可以幫助正確使用分配器?std :: allocator_traits的使用<A>

特別是:

  1. 爲了紀念像std::allocator_traits<A>::propagate_on_container_copy_assignment的類型定義,我要在其中有A類型的成員每個類的特殊成員函數來檢查這些東西呢?或者是有一些包裝類型,我可以使用作爲一個成員,而不是會照顧這個東西?

  2. 如果我想通過在用戶可見對象旁邊存儲額外的數據來減少分配數量,是否適合重新分配這樣的分配器?

template<typename T, typename A> 
class MyClass 
{ 
private: 
    //... 
    struct storage { 
     int m_special_data; 
     T m_obj; 
    }; 
    typedef typename std::allocator_traits<A>::template rebind_alloc<storage> 
     storage_alloc; 
    typedef typename std::allocator_traits<A>::template rebind_traits<storage> 
     storage_traits; 
    storage_alloc m_alloc; 

    static T* alloc(T&& obj) 
    { 
     storage_traits::pointer sp = storage_traits::allocate(m_alloc, 1); 
     sp->m_special_data = 69105; 
     return ::new(&sp->m_obj) T(std::move(obj)); 
    } 
    //... 
}; 

回答

10

我不知道任何東西,使生活更輕鬆,allocator_traits真的可以更容易寫一個allocator,通過提供所有的樣板代碼,但它並不能幫助使用一個分配器。

所以,我可以用在C++ 03和C++ 11的代碼,我加入到GCC 4.7單個分配器API,類模板__gnu_cxx::__alloc_traits規定,在C++ 11模式,並且使用allocator_traits一致的API以C++ 03模式直接在分配器上調用相關的成員函數。

  1. 沒有,沒有包裝或快捷方式,在C++ 11分配器的要求使得容器作者的工作更加複雜。每個容器的要求略有不同,具體取決於它如何管理內存。對於類矢量類型,在複製賦值運算符中,如果propagate_on_container_copy_assignment(POCCA)爲假並且現有容量大於源對象的大小,則可以重新使用現有內存(如果POCCA爲true並且新分配程序爲不等於你不能重新使用舊的內存,因爲在分配器被替換之後將不能再分配它),但該優化對於基於節點的容器(例如列表或映射)沒有多大幫助。

  2. 這看起來差不多吧,雖然你可能想

    A a(m_alloc);  
    std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj)); 
    return &sp->m_obj; 
    

更換

return ::new(&sp->m_obj) T(std::move(obj)); 

正如[container.requirements.general/3的在使用容器陳述分配器使用allocator_traits<A>::construct來創建元素類型T本身,但分配的任何其他類型(例如您的storage)不得使用construct

如果storage本身被構造那麼它將構造storage::m_obj除非該構件是根據其被初始化,如std::aligned_storage<sizeof(T)>一個類型,可以顯式地通過稍後allocator_traits<A>::construct進行初始化。或者,單獨構建需要非平凡構造的每個成員,例如如果storage也有一個string構件:

storage_traits::pointer sp = storage_traits::allocate(m_alloc, 1); 
    sp->m_special_data = 69105; 
    ::new (&sp->m_str) std::string("foobar"); 
    A a(m_alloc);  
    std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj)); 
    return &sp->m_obj; 

m_special_data構件是一個簡單的類型,以便其壽命就存儲被分配用於它開始。 m_strm_obj成員需要進行不平凡的初始化,以便它們的生命週期在其構造函數完成時開始,這分別通過放置new和construct調用完成。

編輯:我最近了解到,標準有缺陷(我已經報)和調用construct不需要使用反彈分配,所以這些線路:

A a(m_alloc);  
    std::allocator_traits<A>::construct(a, &sp->m_obj, std::move(obj)); 

可以替換爲:

std::allocator_traits<storage_alloc>::construct(m_alloc, &sp->m_obj, std::move(obj)); 

這使生活稍微容易一些。

+1

「分配的任何其他類型不能使用'construct'」與建議'storage_traits :: construct(m_alloc,&sp);',或者因爲rebind'沒有計數嗎?如果我構建'存儲'並且它仍然有一個'T'類型的成員,那麼這個成員將與'storage'一起構造,而不是'construct',所以我需要從'''''''''''''''無效'技巧? – aschepler 2012-06-16 22:37:45

+0

@aschepler,+1,很好被發現!現在修復,謝謝 – 2012-06-17 12:32:21

+0

至於你的第二個問題,是的,你可以使用一個聯合體,或者一個適當大小和適當對齊的'char'數組。經驗表明,除了'T'之外,確保'storage'只包含可以構造的數據是很容易的,所以你可以''構造'T'並簡單地給其他部分賦值(通常只是指針和/或整數)。我會編輯我的回答r來反映這一點。 – 2012-06-17 18:33:34

相關問題