问题
Hello I'm trying to transfer/move an element from one list (in the example foo) at (end()-1) to another list (in the example called bar) to position begin().
The only problem is that one of the lists is using an custom made allocator. Which probably results to the following error message:
../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>&)’
My Question here is:
Why is there an error message when splicing list elements from lists with different allocators?
And how can this be fixed?
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:
A list allocates internal nodes by given allocator. Having two lists with different allocators and trying to move elements from one to another means:
- allocate an internal node in
List1
byAllocator1
- move the node to
List2
- later destroy the node in
List2
byAllocator2
Allocators often do some tricks, like allocating more memory than required for additional checks in debug mode e.g. for protection from buffer underruns/overruns. Only allocator knows how much memory it allocated. So Allocator2 is not suitable for deallocation something not allocated by it.
In this particular case Allocator is a template parameter which is part of the type and makes std::list<T, Allocator1>
not compatible with std::list<T, Allocator2>
.
Someone can argue that your CustomAllocator
allocates memory using global operator new
so should be "compatible" with default allocator. I don't know any way to make them compatible from C++ point of view, and I don't think it would be a good idea as it opens a possibility that later another person can decide to enhance you custom allocator a little bit making it incompatible with the default one accidentally. Compiler won't help you here and consequences can be devastating.
来源:https://stackoverflow.com/questions/46051740/why-is-there-an-error-message-when-splicing-list-elements-from-lists-with-differ