始终使用运算符 new/delete 的align_val_t 重载是否安全?

M. *_* P. 6 c++ language-lawyer

我试图编写一个简单的分配函数,我想知道这是否被认为是好的做法,甚至是正确的

template <typename T> T *alloc(size_t count)
{
    return static_cast<T *>(::operator new(
        sizeof(T) * count,
        static_cast<std::align_val_t>(alignof(T)))
    );
}

template <typename T> void free(T *ptr, size_t count)
{
    ::operator delete(
        data,
        sizeof(T) * count,
        static_cast<std::align_val_t>(alignof(T))
    );
}
Run Code Online (Sandbox Code Playgroud)

是否保证align_val_t总是可以选择重载而不是没有align_val_t后者正确的重载?这对于用户替换这些可替换重载所需的行为仍然适用吗?

(假设大小相乘不会溢出。)

use*_*170 5

我认为给出的示例没有理由无法正确分配内存。 [new.delete.single][basic.std.dynamic.allocation]/3定义了此运算符重载的行为,new并不强制要求仅将其用于需要它的类型。

\n

当且仅当类型需要扩展对齐时才优先使用重载std::align_val_t是由于[expr.new]/19

\n
\n

重载解析是在通过组装参数列表创建的函数调用上执行的。第一个参数是请求的空间量,类型为std\xe2\x80\x8b::\xe2\x80\x8bsize_\xc2\xadt。如果分配的对象的类型具有新扩展对齐方式,则下一个参数是该类型的对齐方式,并且具有类型std\xe2\x80\x8b::\xe2\x80\x8balign_\xc2\xadval_\xc2\xadt。如果使用new-placement语法,则其表达式列表中的 _initializer-clause_s是后续参数。

\n
\n

然而,这段话紧接着是:

\n
\n

如果没有找到匹配的函数那么

\n
    \n
  • (19.1) 如果分配的对象类型具有新扩展对齐方式,则从参数列表中删除对齐方式参数;
  • \n
  • (19.2) 否则,作为类型对齐且具有类型的参数std\xe2\x80\x8b::\xe2\x80\x8balign_\xc2\xadval_\xc2\xadt将立即添加到参数列表中第一个参数之后;
  • \n
\n

然后再次进行重载决策。

\n
\n

这表示new如果首选版本不可用,操作员应该回退到其他版本。这意味着该标准旨在支持仅其中一种重载可用的情况。这主要发生在定义自己的operator new成员([expr.new]/12)的类中。

\n

不过,您的分配函数还有一些其他问题。除了已经注意到的算术溢出之外,它无法调用分配的存储中的构造函数,这意味着它不会返回指向完全构造的对象的指针;new您可能还需要调用放置重载。但这不是重点。

\n