2017-09-05 113 views
0

你好我試圖從一個列表(在例子foo中)在(end() - 1)到另一個列表(在這個例子中稱爲bar)中移動一個元素到位置begin()。爲什麼在使用不同分配器的列表中拼接列表元素時會出現錯誤消息?這怎麼解決?

唯一的問題是其中一個列表正在使用自定義分配器。 這可能會導致以下錯誤信息:這裏

../src/TestAllocator.cpp:120:28: error: 
no matching function for call to 
‘std::list<int>::splice ( std::_List_iterator<int>&, 
          std::list<int, CustomAllocator<int> >&, 
          std::_List_iterator<int>&)’ 

我的問題是:

  • 爲什麼會出現錯誤信息拼接從不同的分配器名單 列表中的元素是什麼時候?

  • 如何解決這個問題?

Code on Coliru

#include <limits> // numeric_limits 
#include <iostream> 
#include <typeinfo> // typeid 

// container 
#include <vector> 
#include <list> 
#include <forward_list> 

/// state stored as static member(s) of an auxiliary(=hilfs/zusatz) class 
struct CustomAllocatorState { 
    static std::size_t m_totalMemAllocated; 
}; 
std::size_t CustomAllocatorState::m_totalMemAllocated = 0; 

/// @brief The @a custom allocator 
/// @tparam T Type of allocated object 
template<typename T> 
class CustomAllocator { 
public: 
    // type definitions 
    typedef std::size_t  size_type;   /** Quantities of elements */ 
    typedef std::ptrdiff_t difference_type; /** Difference between two pointers */ 
    typedef T*    pointer;   /** Pointer to element */ 
    typedef const T*  const_pointer;  /** Pointer to constant element */ 
    typedef T&    reference;   /** Reference to element */ 
    typedef const T&  const_reference; /** Reference to constant element */ 
    typedef T    value_type;   /** Element type */ 

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


    CustomAllocator() throw() { 
     std::cout << "construct " << typeid(T).name() << std::endl; 
    } 

    CustomAllocator(const CustomAllocator&) throw() { 
     std::cout << "copy construct " << typeid(T).name() << std::endl; 
    } 

    template<class U> 
    CustomAllocator() throw() { 
    } 

    ~CustomAllocator() throw() {} 


    // allocate but don't initialize num elements of type T 
    pointer allocate(size_type num, const void* = 0) { 
     CustomAllocatorState::m_totalMemAllocated += num * sizeof(T); 
     // print message and allocate memory with global new 
     std::cout << "allocate " << num << " element(s)" << " of size " 
       << sizeof(T) << std::endl; 
     pointer ret = (pointer) (::operator new(num * sizeof(T))); 
     std::cout << " allocated at: " << (void*) ret << std::endl; 
     return ret; 
    } 

    // deallocate storage p of deleted elements 
    void deallocate(pointer p, size_type num) { 
     CustomAllocatorState::m_totalMemAllocated -= num * sizeof(T); 
     // print message and deallocate memory with global delete 
     std::cout << "deallocate " << num << " element(s)" << " of size " 
       << sizeof(T) << " at: " << (void*) p << std::endl; 
     ::operator delete((void*) p); 
    } 

    // initialize elements of allocated storage p with value value 
    // no need to call rebind with this variadic template anymore in C++11 
    template<typename _U, typename ... _Args> 
    void construct(_U* p, _Args&&... args) { 
     ::new ((void *) p) _U(std::forward<_Args>(args)...); 
    } 

    // destroy elements of initialized storage p 
    template<typename _U> 
    void destroy(_U* p) { 
     p->~_U(); 
    } 

    // return address of values 
    pointer address (reference value) const { 
     return &value; 
    } 
    const_pointer address (const_reference value) const { 
     return &value; 
    } 


    // return maximum number of elements that can be allocated 
    size_type max_size() const throw() { 
     return std::numeric_limits<std::size_t>::max()/sizeof(T); 
    } 
}; 

template<typename T, typename U> 
inline bool operator==(const CustomAllocator<T>&, const CustomAllocator<U>&) { 
    return true; 
} 

template<typename T, typename U> 
inline bool operator!=(const CustomAllocator<T>&, const CustomAllocator<U>&) { 
    return false; 
} 

int main() { 
    std::list<int, CustomAllocator<int>> foo; 
    std::list<int> bar; // aka std::list<int, std::allocator<int> > bar; 
    foo.push_back(23); 
    foo.push_back(12); 
    foo.push_back(8); 

    // transfer/move element in foo at end() to list bar at position begin() 
    auto pos = bar.begin(); 
    auto other = foo; 
    auto it = --(foo.end()); 
    bar.splice(pos, foo, it); // here the error: no matching function for call 

    std::cout << "---" << std::endl; 

    // debug output 
    std::cout << "Foo: "; 
    for (auto x : foo) 
     std::cout << x << " "; 
    std::cout << std::endl; 

    std::cout << "Bar: "; 
    for (auto x : bar) 
     std::cout << x << " "; 
    std::cout << std::endl; 

    std::cout << "alloc_count: " << CustomAllocatorState::m_totalMemAllocated << std::endl; 
    std::cout << "---" << std::endl; 
    std::cout << '\n'; 
    return 0; 
} 
+1

最後一個元素在'end()' – CinCout

+0

沒有真正的^^我會糾正 - (foo.end())當然是 – Robert

+3

不知道編譯器錯誤,但如果兩個容器中的分配器不相等,它是*未定義的行爲*。 – CinCout

回答

2

列表分配由下式給出分配器內部節點。具有不同分配器兩個列表,並試圖從一個移動元件,以另一種方式:

  • 分配在List1List2
  • 內部節點通過Allocator1
  • 移動節點以後通過Allocator2破壞節點List2

分配器通常會執行一些技巧,比如分配更多的內存,而不是在調試模式下進行附加檢查所需的內存用於防止緩衝區欠載/超限。只有分配器知道它分配了多少內存。所以Allocator2不適合於未分配的分配。

在這種特殊情況下,分配器是一個模板參數,它是類型的一部分,並且使std::list<T, Allocator1>std::list<T, Allocator2>不兼容。

有人可能會爭辯說,您的CustomAllocator使用全局分配內存operator new所以應該與默認分配器「兼容」。我不知道有什麼方法可以使它們從C++的角度來兼容,我不認爲這會是一個好主意,因爲它可能讓以後的另一個人可以決定讓自定義分配器稍微增加一點點與默認的不兼容。編譯器在這裏不會幫你,後果可能是毀滅性的。

+0

編譯器錯誤是否合理? AFAIK它是UB,但這是一種運行時行爲。 – CinCout

+0

@CinCout:是的,編譯器說它得到了一個不兼容類型的迭代器 –

+1

*「如果:get_allocator()!= other.get_allocator()'。」,行爲是不確定的。*沒有提到編譯器錯誤。 http://en.cppreference.com/w/cpp/container/list/splice – CinCout

相關問題