156 lines · plain
1=========================2C++ Type Aware Allocators3=========================4 5.. contents::6 :local:7 8Introduction9============10 11Clang includes an implementation of P2719 "Type-aware allocation and deallocation12functions".13 14This is a feature that extends the semantics of `new`, `new[]`, `delete` and15`delete[]` operators to expose the type being allocated to the operator. This16can be used to customize allocation of types without needing to modify the17type declaration, or via template definitions fully generic type aware18allocators.19 20P2719 introduces a type-identity tag as valid parameter type for all allocation21operators. This tag is a default initialized value of type `std::type_identity<T>`22where T is the type being allocated or deallocated. Unlike the other placement23arguments this tag is passed as the first parameter to the operator.24 25The most basic use case is as follows26 27.. code-block:: c++28 29 #include <new>30 #include <type_traits>31 32 struct S {33 // ...34 };35 36 void *operator new(std::type_identity<S>, size_t, std::align_val_t);37 void operator delete(std::type_identity<S>, void *, size_t, std::align_val_t);38 39 void f() {40 S *s = new S; // calls ::operator new(std::type_identity<S>(), sizeof(S), alignof(S))41 delete s; // calls ::operator delete(std::type_identity<S>(), s, sizeof(S), alignof(S))42 }43 44While this functionality alone is powerful and useful, the true power comes45by using templates. In addition to adding the type-identity tag, P2719 allows46the tag parameter to be a dependent specialization of `std::type_identity`,47updates the overload resolution rules to support full template deduction and48constraint semantics, and updates the definition of usual deallocation functions49to include `operator delete` definitions that are templatized on the50type-identity tag.51 52This allows arbitrarily constrained definitions of the operators that resolve53as would be expected for any other template function resolution, e.g (only54showing `operator new` for brevity)55 56.. code-block:: c++57 58 template <typename T, unsigned Size> struct Array {59 T buffer[Size];60 };61 62 // Starting with a concrete type63 void *operator new(std::type_identity<Array<int, 5>>, size_t, std::align_val_t);64 65 // Only care about five element arrays66 template <typename T>67 void *operator new(std::type_identity<Array<T, 5>>, size_t, std::align_val_t);68 69 // An array of N floats70 template <unsigned N>71 void *operator new(std::type_identity<Array<float, N>>, size_t, std::align_val_t);72 73 // Any array74 template <typename T, unsigned N>75 void *operator new(std::type_identity<Array<T, N>>, size_t, std::align_val_t);76 77 // A handy concept78 template <typename T> concept Polymorphic = std::is_polymorphic_v<T>;79 80 // Only applies is T is Polymorphic81 template <Polymorphic T, unsigned N>82 void *operator new(std::type_identity<Array<T, N>>, size_t, std::align_val_t);83 84 // Any even length array85 template <typename T, unsigned N>86 void *operator new(std::type_identity<Array<T, N>>, size_t, std::align_val_t)87 requires(N%2 == 0);88 89Operator selection then proceeds according to the usual rules for choosing90the best/most constrained match.91 92Any declaration of a type aware operator new or operator delete must include a93matching complementary operator defined in the same scope.94 95Notes96=====97 98Unconstrained Global Operators99------------------------------100 101Declaring an unconstrained type aware global operator `new` or `delete` (or102`[]` variants) creates numerous hazards, similar to, but different from, those103created by attempting to replace the non-type aware global operators. For that104reason unconstrained operators are strongly discouraged.105 106Mismatching Constraints107-----------------------108 109When declaring global type aware operators you should ensure the constraints110applied to new and delete match exactly, and declare them together. This111limits the risk of having mismatching operators selected due to differing112constraints resulting in changes to prioritization when determining the most113viable candidate.114 115Declarations Across Libraries116-----------------------------117 118Declaring a typed allocator for a type in a separate TU or library creates119similar hazards as different libraries and TUs may see (or select) different120definitions.121 122Under this model something like this would be risky123 124.. code-block:: c++125 126 template<typename T>127 void *operator new(std::type_identity<std::vector<T>>, size_t, std::align_val_t);128 129However this hazard is not present simply due to the use of the a type from130another library:131 132.. code-block:: c++133 134 template<typename T>135 struct MyType {136 T thing;137 };138 template<typename T>139 void *operator new(std::type_identity<MyType<std::vector<T>>>, size_t, std::align_val_t);140 141Here we see `std::vector` being used, but that is not the actual type being142allocated.143 144Implicit and Placement Parameters145---------------------------------146 147Type aware allocators are always passed both the implicit alignment and size148parameters in all cases. Explicit placement parameters are supported after the149mandatory implicit parameters.150 151Publication152===========153 154`Type-aware allocation and deallocation functions <https://wg21.link/P2719>`_.155Louis Dionne, Oliver Hunt.156