c++17:至少必须替换哪些版本的全局运算符 new/delete 才能涵盖所有情况?

c++17: which versions of global operators new/delete must be replaced at least to cover all cases?

当全局重载运算符 new 和 delete 时,需要实现哪些版本以覆盖所有情况?

似乎在 c++17 之前实现 void* operator new(size_t bytes)void operator delete(void* ptr) 就足够了,因为默认实现的数组版本和非抛出版本是使用这两个函数实现的。

但是c++17呢?例如 https://en.cppreference.com/w/cpp/memory/new/operator_new 没有提到 (4) 将通过调用 (3) 来实现(而提到 (2) 是通过调用 (1) 来实现)。

那么,在 c++17 中,需要让对 new 和 delete 版本的所有调用(除了具有本地覆盖的 类)被替换覆盖的最少运算符是多少?

来自 cppreference https://en.cppreference.com/w/cpp/memory/new/operator_new#Global_replacements (And a similar note with operator delete):

The standard library implementations of the nothrow versions (5-8) directly calls the corresponding throwing versions (1-4). The standard library implementation of the throwing array versions (2,4) directly calls the corresponding single-object version (1,3). Thus, replacing the throwing single object allocation functions is sufficient to handle all allocations.

所以你需要替换这些函数:

void* operator new(std::size_t count);
void* operator new(std::size_t count, std::align_val_t al);
void operator delete(void* ptr) noexcept;
void operator delete(void* ptr, std::align_val_t al) noexcept;

其余的将根据这四个来实施。

如果您没有对齐的 malloc-equivalent 来替换对齐的版本,这里是根据 [=26] 对齐的 new/delete 的简单实现=]版本:

#include <cstddef>
#include <new>
#include <memory>

void* operator new(std::size_t count, std::align_val_t al) {
    std::size_t align = static_cast<std::size_t>(al);
    if (align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) [[unlikely]] return operator new(count);
    std::size_t actually_allocating = align + count;
    if (actually_allocating < count || (actually_allocating += sizeof(void*)) < (align + count)) [[unlikely]] {
        // overflow
        throw std::bad_alloc();
    }

    void* unaligned = operator new(actually_allocating);
    void* aligned = unaligned;
    std::align(align, 0, aligned, actually_allocating);
    // Store a pointer to the start of the aligned memory, to be retrieved by delete
    ::new (static_cast<void*>(static_cast<char*>(aligned) - sizeof(void*))) void*(unaligned);
    return aligned;
}

void operator delete(void* ptr, std::align_val_t al) noexcept {
    if (static_cast<std::size_t>(al) > __STDCPP_DEFAULT_NEW_ALIGNMENT__) [[likely]] {
        ptr = *static_cast<void**>(static_cast<void*>(static_cast<char*>(ptr) - sizeof(void*)));
    }
    operator delete(ptr);
}