mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-06 08:46:45 +08:00
ROOT CAUSE:
The allocate() function was incorrectly constructing objects during memory
allocation, violating C++ allocator requirements. MSVC's std::_Tree_node has
a deleted default constructor, causing compilation failure.
CHANGES:
1. container_allocator::allocate() - Now only allocates raw memory without
constructing objects (removed mem::$new and ipc::construct calls)
2. container_allocator::deallocate() - Now only frees memory without
destroying objects (removed mem::$delete and ipc::destroy_n calls)
WHY THIS FIXES THE ISSUE:
- C++ allocator semantics require strict separation:
* allocate() -> raw memory allocation
* construct() -> object construction
* destroy() -> object destruction
* deallocate() -> memory deallocation
- std::map and other containers call construct() with proper arguments
(key, value) to initialize nodes, not allocate()
- std::_Tree_node in MSVC has no default constructor (= delete), so
attempting to construct it without arguments always fails
- The previous code tried to default-construct objects in allocate(),
which is both semantically wrong and impossible for _Tree_node
PREVIOUS FIX (uninitialized.h):
The earlier fix to uninitialized.h was insufficient - even with correct
T() vs T{} handling, you cannot default-construct a type with deleted
default constructor.
Fixes MSVC 2017 compilation error:
error C2280: attempting to reference a deleted function
102 lines
3.1 KiB
C++
102 lines
3.1 KiB
C++
/**
|
|
* \file libipc/container_allocator.h
|
|
* \author mutouyun (orz@orzz.org)
|
|
* \brief An allocator that can be used by all standard library containers.
|
|
*/
|
|
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <utility>
|
|
#include <limits>
|
|
|
|
#include "libipc/imp/uninitialized.h"
|
|
#include "libipc/mem/new.h"
|
|
|
|
namespace ipc {
|
|
namespace mem {
|
|
|
|
/**
|
|
* \brief An allocator that can be used by all standard library containers.
|
|
*
|
|
* \see https://en.cppreference.com/w/cpp/memory/allocator
|
|
* https://en.cppreference.com/w/cpp/memory/polymorphic_allocator
|
|
*/
|
|
template <typename T>
|
|
class container_allocator {
|
|
|
|
template <typename U>
|
|
friend class container_allocator;
|
|
|
|
public:
|
|
// type definitions
|
|
typedef T value_type;
|
|
typedef value_type * pointer;
|
|
typedef const value_type *const_pointer;
|
|
typedef value_type & reference;
|
|
typedef const value_type &const_reference;
|
|
typedef std::size_t size_type;
|
|
typedef std::ptrdiff_t difference_type;
|
|
|
|
// the other type of std_allocator
|
|
template <typename U>
|
|
struct rebind {
|
|
using other = container_allocator<U>;
|
|
};
|
|
|
|
container_allocator() noexcept {}
|
|
|
|
// construct by copying (do nothing)
|
|
container_allocator (container_allocator<T> const &) noexcept {}
|
|
container_allocator& operator=(container_allocator<T> const &) noexcept { return *this; }
|
|
|
|
// construct from a related allocator (do nothing)
|
|
template <typename U> container_allocator (container_allocator<U> const &) noexcept {}
|
|
template <typename U> container_allocator &operator=(container_allocator<U> const &) noexcept { return *this; }
|
|
|
|
container_allocator (container_allocator &&) noexcept = default;
|
|
container_allocator& operator=(container_allocator &&) noexcept = default;
|
|
|
|
constexpr size_type max_size() const noexcept {
|
|
return (std::numeric_limits<size_type>::max)() / sizeof(value_type);
|
|
}
|
|
|
|
pointer allocate(size_type count) noexcept {
|
|
if (count == 0) return nullptr;
|
|
if (count > this->max_size()) return nullptr;
|
|
// Allocate raw memory without constructing objects
|
|
// Construction should be done by construct() member function
|
|
void *p = mem::alloc(sizeof(value_type) * count);
|
|
return static_cast<pointer>(p);
|
|
}
|
|
|
|
void deallocate(pointer p, size_type count) noexcept {
|
|
if (count == 0) return;
|
|
if (count > this->max_size()) return;
|
|
// Deallocate raw memory without destroying objects
|
|
// Destruction should be done by destroy() member function before deallocate
|
|
mem::free(p, sizeof(value_type) * count);
|
|
}
|
|
|
|
template <typename... P>
|
|
static void construct(pointer p, P && ... params) {
|
|
std::ignore = ipc::construct<T>(p, std::forward<P>(params)...);
|
|
}
|
|
|
|
static void destroy(pointer p) {
|
|
std::ignore = ipc::destroy(p);
|
|
}
|
|
};
|
|
|
|
template <typename T, typename U>
|
|
constexpr bool operator==(container_allocator<T> const &, container_allocator<U> const &) noexcept {
|
|
return true;
|
|
}
|
|
|
|
template <typename T, typename U>
|
|
constexpr bool operator!=(container_allocator<T> const &, container_allocator<U> const &) noexcept {
|
|
return false;
|
|
}
|
|
|
|
} // namespace mem
|
|
} // namespace ipc
|