Is it safe to always use the align_val_t overload of operator new/delete?

Viewed 248

I was trying to write a simple allocation function and I was wondering if this is considered good practice or even correct

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))
    );
}

Is it guaranteed that the align_val_t overloads can always be chosen instead of the overloads without align_val_t where the latter would be correct? Does this still hold true for the required behavior of user-replacements of these replaceable overloads?

(Assuming the multiplication on the size does not overflow.)

1 Answers

I see no reason why the examples given would fail to correctly allocate memory. [new.delete.single] and [basic.std.dynamic.allocation]/3, which define the behaviour of this new operator overload do not mandate using it only for types which require it.

The preference for using the std::align_val_t overload if and only if the type requires an extended alignment is due to [expr.new]/19:

Overload resolution is performed on a function call created by assembling an argument list. The first argument is the amount of space requested, and has type std​::​size_­t. If the type of the allocated object has new-extended alignment, the next argument is the type's alignment, and has type std​::​align_­val_­t. If the new-placement syntax is used, the _initializer-clause_s in its expression-list are the succeeding arguments.

However, this passage is immediately followed by:

If no matching function is found then

  • (19.1) if the allocated object type has new-extended alignment, the alignment argument is removed from the argument list;
  • (19.2) otherwise, an argument that is the type's alignment and has type std​::​align_­val_­t is added into the argument list immediately after the first argument;

and then overload resolution is performed again.

which says the new operator ought to fall back to the other version if the preferred one is not available. This implies the standard intends to support situations where only one of the overloads is available. This mostly arises for classes that define their own operator new member ([expr.new]/12).

Your allocation function has some other problems, though. Other than the already-noticed arithmetic overflow, it fails to invoke the constructor in the storage allocated, which means it does not return a pointer to fully-constructed objects; you will probably need to invoke the placement overload of new as well. But that is beside the main point.

Related