编译器错误 C2956
常用解除分配函数 'function'会被选为放置解除分配函数。
为放置新表达式找到的解除分配函数与一个常用解除分配函数匹配。 隐式编译器生成的解除分配或显式 delete
(或 delete[]
)将使用错误的解除分配函数。
备注
错误 C2956 指示使用放置置新表达式(采用参数的表达式 new
)的方式可能会导致内存泄漏或运行时崩溃。 它通常意味着无法以典型方式删除生成的值。 也就是说,代码中的显式 delete
(或 delete[]
)表达式,或者在构造函数引发异常时隐式解除分配,可以调用错误 operator delete
或提供错误的参数。
C++ 标准将常用的解除分配函数指定为 operator delete
的重载,或者采用类型 std::size_t
的其他参数的 operator delete[]
(C++14 或更高版本)、std::align_val_t
(C++17 或更高版本),以及 std::destroying_delete_t
(C++20 或更高版本)。 使用放置新表达式时,编译器会查找(在第一个后)采用相同参数的匹配 operator delete
函数。 如果找到一个并且其签名与常用的解除分配函数匹配,编译器将报告错误 C2956。
解决问题的方式取决于你的意图。 例如,在 C++ 11 中,可以定义 operator new
重载,该重载采用类中的额外 size_t
参数将值传递给分配器。 在 C++ 14 中,同一代码现在会导致错误:
#include <new>
struct T {
void* operator new(std::size_t, std::size_t); // Placement allocation function
void operator delete(void*, std::size_t); // now considered a usual deallocation function
};
T* p = new (0) T; // error: usual deallocation function would be chosen as placement deallocation function
如果打算为对象指定过度对齐的内存,则可以使用 alignas
直接在类型上指定对齐方式。 有关 alignas
的详细信息,请参阅对齐:
如果打算为堆分配的本机类型或数组指定过度对齐的内存,请将其包装在具有 alignas
说明符的 struct
或 class
中。 然后,普通 new
和 delete
表达式可以分配并解除分配具有预期对齐方式的实例。
示例
在此示例中,new-expression
使用具有类型 std::align_val_t
参数的放置语法。 但是,由于类型 T
未指定对齐要求,T*
上的 delete-expression
不会调用匹配的过度对齐解除分配函数。 相反,编译器将调用通常的解除分配函数 void operator delete(void* ptr) noexcept
,该函数不会处理过度对齐的分配。 编译器报告使用此放置 new
出现的错误,而不是导致崩溃或内存泄漏:
#include <new>
struct T {};
int main()
{
T* p = new (std::align_val_t{64}) T; // C2956
delete p; // ordinary, not over-aligned delete
}
若要解决此问题,请将 alignas
说明符应用于 T
:
#include <new>
struct alignas(64) T {};
int main()
{
T* p = new T; // invokes ::operator new(std::size_t, std::align_val_t)
delete p; // now invokes ::operator delete(void*, std::align_val_t)
}