为什么从不同分配器的列表中拼接列表元素时会出现错误消息?如何解决这个问题?
Why is there an error message when splicing list elements from lists with different allocators? And how can this be fixed?
你好,我正在尝试 transfer/move 一个列表(在示例 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>&)’
我的问题是:
为什么从列表中拼接列表元素会出现错误信息
使用不同的分配器?
如何解决?
#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;
}
列表通过给定的分配器分配内部节点。拥有两个具有不同分配器的列表并尝试将元素从一个列表移动到另一个列表意味着:
- 通过
Allocator1
在List1
中分配一个内部节点
- 将节点移动到
List2
- 稍后由
Allocator2
销毁 List2
中的节点
分配器经常做一些技巧,比如分配比调试模式下额外检查所需的更多的内存,例如用于保护缓冲区 underruns/overruns。只有分配器知道它分配了多少内存。所以 Allocator2 不适合释放不是由它分配的东西。
在这种特殊情况下,分配器是一个模板参数,它是类型的一部分,使 std::list<T, Allocator1>
与 std::list<T, Allocator2>
不兼容。
有人可能会争辩说您的 CustomAllocator
使用全局 operator new
分配内存,因此应该 "compatible" 使用默认分配器。从 C++ 的角度来看,我不知道有什么方法可以使它们兼容,而且我认为这不是一个好主意,因为它打开了一种可能性,以后另一个人可以决定稍微增强您的自定义分配器使其成为可能不小心与默认的不兼容。编译器不会在这里帮助你,后果可能是毁灭性的。
你好,我正在尝试 transfer/move 一个列表(在示例 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>&)’
我的问题是:
为什么从列表中拼接列表元素会出现错误信息 使用不同的分配器?
如何解决?
#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;
}
列表通过给定的分配器分配内部节点。拥有两个具有不同分配器的列表并尝试将元素从一个列表移动到另一个列表意味着:
- 通过
Allocator1
在 - 将节点移动到
List2
- 稍后由
Allocator2
销毁
List1
中分配一个内部节点
List2
中的节点
分配器经常做一些技巧,比如分配比调试模式下额外检查所需的更多的内存,例如用于保护缓冲区 underruns/overruns。只有分配器知道它分配了多少内存。所以 Allocator2 不适合释放不是由它分配的东西。
在这种特殊情况下,分配器是一个模板参数,它是类型的一部分,使 std::list<T, Allocator1>
与 std::list<T, Allocator2>
不兼容。
有人可能会争辩说您的 CustomAllocator
使用全局 operator new
分配内存,因此应该 "compatible" 使用默认分配器。从 C++ 的角度来看,我不知道有什么方法可以使它们兼容,而且我认为这不是一个好主意,因为它打开了一种可能性,以后另一个人可以决定稍微增强您的自定义分配器使其成为可能不小心与默认的不兼容。编译器不会在这里帮助你,后果可能是毁灭性的。