2 * Copyright 2004-present Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <initializer_list>
21 #include <type_traits>
24 #include <folly/Launder.h>
25 #include <folly/Traits.h>
26 #include <folly/Utility.h>
29 * An instance of `Replaceable<T>` wraps an instance of `T`.
31 * You access the inner `T` instance with `operator*` and `operator->` (as if
32 * it were a smart pointer).
34 * `Replaceable<T>` adds no indirection cost and performs no allocations.
36 * `Replaceable<T>` has the same size and alignment as `T`.
38 * You can replace the `T` within a `Replaceable<T>` using the `emplace` method
39 * (presuming that it is constructible and destructible without throwing
40 * exceptions). If the destructor or constructor you're using could throw an
41 * exception you should use `Optional<T>` instead, as it's not a logic error
42 * for that to be empty.
44 * Frequently Asked Questions
45 * ==========================
47 * Why does this need to be so complicated?
48 * ----------------------------------------
50 * If a `T` instance contains `const`-qualified member variables or reference
51 * member variables we can't safely replace a `T` instance by destructing it
52 * manually and using placement new. This is because compilers are permitted to
53 * assume that the `const` or reference members of a named, referenced, or
54 * pointed-to object do not change.
56 * For pointed-to objects in allocated storage you can use the pointer returned
57 * by placement new or use the `launder` function to get a pointer to the new
58 * object. Note that `launder` doesn't affect its argument, it's still
59 * undefined behaviour to use the original pointer. And none of this helps if
60 * the object is a local or a member variable because the destructor call will
61 * not have been laundered. In summary, this is the only way to use placement
62 * new that is both simple and safe:
66 * pT = ::new (pT) T(...);
69 * What are the other safe solutions to this problem?
70 * --------------------------------------------------
72 * * Ask the designer of `T` to de-`const` and -`reference` the members of `T`.
73 * - Makes `T` harder to reason about
74 * - Can reduce the performance of `T` methods
75 * - They can refuse to make the change
76 * * Put the `T` on the heap and use a raw/unique/shared pointer.
77 * - Adds a level of indirection, costing performance.
78 * - Harder to reason about your code as you need to check for nullptr.
79 * * Put the `T` in an `Optional`.
80 * - Harder to reason about your code as you need to check for None.
81 * * Pass the problem on, making the new code also not-replaceable
82 * - Contagion is not really a solution
84 * Are there downsides to this?
85 * ----------------------------
87 * There is a potential performance penalty after converting `T` to
88 * `Replaceable<T>` if you have non-`T`-member-function code which repeatedly
89 * examines the value of a `const` or `reference` data member of `T`, because
90 * the compiler now has to look at the value each time whereas previously it
91 * was permitted to load it once up-front and presume that it could never
97 * Don't store a reference to the `T` within a `Replaceable<T>` unless you can
98 * show that its lifetime does not cross an `emplace` call. For safety a
99 * reasonable rule is to always use `operator*()` to get a fresh temporary each
100 * time you need a `T&.
102 * If you store a pointer to the `T` within a `Replaceable<T>` you **must**
103 * launder it after each call to `emplace` before using it. Again you can
104 * reasonably choose to always use `operator->()` to get a fresh temporary each
105 * time you need a `T*.
107 * Thus far I haven't thought of a good reason to use `Replaceable<T>` or
108 * `Replaceable<T> const&` as a function parameter type.
110 * `Replaceable<T>&` can make sense to pass to a function that conditionally
111 * replaces the `T`, where `T` has `const` or reference member variables.
113 * The main use of `Replaceable<T>` is as a class member type or a local type
114 * in long-running functions.
116 * It's probably time to rethink your design choices if you end up with
117 * `Replaceable<Replaceable<T>>`, `Optional<Replaceable<T>>`,
118 * `Replaceable<Optional<T>>`, `unique_ptr<Replaceable<T>>` etc. except as a
119 * result of template expansion.
126 namespace replaceable_detail {
127 /* Mixin templates to give `replaceable<T>` the following properties:
129 * 1. Trivial destructor if `T` has a trivial destructor; user-provided
131 * 2. Move constructor if `T` has a move constructor; deleted otherwise
132 * 3. Move assignment operator if `T` has a move constructor; deleted
134 * 4. Copy constructor if `T` has a copy constructor; deleted otherwise
135 * 5. Copy assignment operator if `T` has a copy constructor; deleted
138 * Has to be done in this way because we can't `enable_if` them away
142 bool /* true iff destructible */,
143 bool /* true iff trivially destructible */>
146 /* Destructible and trivially destructible */
148 struct dtor_mixin<T, true, true> {};
150 /* Destructible and not trivially destructible */
152 struct dtor_mixin<T, true, false> {
153 dtor_mixin() = default;
154 dtor_mixin(dtor_mixin&&) = default;
155 dtor_mixin(dtor_mixin const&) = default;
156 dtor_mixin& operator=(dtor_mixin&&) = default;
157 dtor_mixin& operator=(dtor_mixin const&) = default;
158 ~dtor_mixin() noexcept(std::is_nothrow_destructible<T>::value) {
159 T* destruct_ptr = launder(reinterpret_cast<T*>(
160 reinterpret_cast<Replaceable<T>*>(this)->storage_));
165 /* Not destructible */
166 template <class T, bool A>
167 struct dtor_mixin<T, false, A> {
168 dtor_mixin() = default;
169 dtor_mixin(dtor_mixin&&) = default;
170 dtor_mixin(dtor_mixin const&) = default;
171 dtor_mixin& operator=(dtor_mixin&&) = default;
172 dtor_mixin& operator=(dtor_mixin const&) = default;
173 ~dtor_mixin() = delete;
178 bool /* true iff default constructible */,
179 bool /* true iff move constructible */>
180 struct default_and_move_ctor_mixin;
182 /* Not default-constructible and not move-constructible */
184 struct default_and_move_ctor_mixin<T, false, false> {
185 default_and_move_ctor_mixin() = delete;
186 default_and_move_ctor_mixin(default_and_move_ctor_mixin&&) = delete;
187 default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;
188 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =
190 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =
194 inline explicit default_and_move_ctor_mixin(int) {}
197 /* Default-constructible and move-constructible */
199 struct default_and_move_ctor_mixin<T, true, true> {
200 inline default_and_move_ctor_mixin() noexcept(
201 std::is_nothrow_constructible<T>::value) {
202 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T();
204 inline default_and_move_ctor_mixin(
205 default_and_move_ctor_mixin&&
206 other) noexcept(std::is_nothrow_constructible<T, T&&>::value) {
207 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
208 T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
210 default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;
211 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =
213 inline default_and_move_ctor_mixin& operator=(
214 default_and_move_ctor_mixin const&) = default;
217 inline explicit default_and_move_ctor_mixin(int) {}
220 /* Default-constructible and not move-constructible */
222 struct default_and_move_ctor_mixin<T, true, false> {
223 inline default_and_move_ctor_mixin() noexcept(
224 std::is_nothrow_constructible<T>::value) {
225 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T();
227 default_and_move_ctor_mixin(default_and_move_ctor_mixin&&) = delete;
228 default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;
229 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =
231 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =
235 inline explicit default_and_move_ctor_mixin(int) {}
238 /* Not default-constructible but is move-constructible */
240 struct default_and_move_ctor_mixin<T, false, true> {
241 default_and_move_ctor_mixin() = delete;
242 inline default_and_move_ctor_mixin(
243 default_and_move_ctor_mixin&&
244 other) noexcept(std::is_nothrow_constructible<T, T&&>::value) {
245 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
246 T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
248 default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;
249 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =
251 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =
255 inline explicit default_and_move_ctor_mixin(int) {}
258 template <class T, bool /* true iff destructible and move constructible */>
259 struct move_assignment_mixin;
261 /* Not (destructible and move-constructible) */
263 struct move_assignment_mixin<T, false> {
264 move_assignment_mixin() = default;
265 move_assignment_mixin(move_assignment_mixin&&) = default;
266 move_assignment_mixin(move_assignment_mixin const&) = default;
267 move_assignment_mixin& operator=(move_assignment_mixin&&) = delete;
268 move_assignment_mixin& operator=(move_assignment_mixin const&) = default;
271 /* Both destructible and move-constructible */
273 struct move_assignment_mixin<T, true> {
274 move_assignment_mixin() = default;
275 move_assignment_mixin(move_assignment_mixin&&) = default;
276 move_assignment_mixin(move_assignment_mixin const&) = default;
277 inline move_assignment_mixin&
278 operator=(move_assignment_mixin&& other) noexcept(
279 std::is_nothrow_destructible<T>::value&&
280 std::is_nothrow_move_constructible<T>::value) {
281 T* destruct_ptr = launder(reinterpret_cast<T*>(
282 reinterpret_cast<Replaceable<T>*>(this)->storage_));
284 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
285 T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
288 move_assignment_mixin& operator=(move_assignment_mixin const&) = default;
291 template <class T, bool /* true iff copy constructible */>
292 struct copy_ctor_mixin;
294 /* Not copy-constructible */
296 struct copy_ctor_mixin<T, false> {
297 copy_ctor_mixin() = default;
298 copy_ctor_mixin(copy_ctor_mixin&&) = default;
299 copy_ctor_mixin(copy_ctor_mixin const&) = delete;
300 copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default;
301 copy_ctor_mixin& operator=(copy_ctor_mixin const&) = delete;
304 /* Copy-constructible */
306 struct copy_ctor_mixin<T, true> {
307 copy_ctor_mixin() = default;
308 inline copy_ctor_mixin(copy_ctor_mixin const& other) noexcept(
309 std::is_nothrow_constructible<T, T const&>::value) {
310 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
311 T(*reinterpret_cast<Replaceable<T> const&>(other));
313 copy_ctor_mixin(copy_ctor_mixin&&) = default;
314 copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default;
315 copy_ctor_mixin& operator=(copy_ctor_mixin const&) = default;
318 template <class T, bool /* true iff destructible and copy constructible */>
319 struct copy_assignment_mixin;
321 /* Not (destructible and copy-constructible) */
323 struct copy_assignment_mixin<T, false> {
324 copy_assignment_mixin() = default;
325 copy_assignment_mixin(copy_assignment_mixin&&) = default;
326 copy_assignment_mixin(copy_assignment_mixin const&) = default;
327 copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default;
328 copy_assignment_mixin& operator=(copy_assignment_mixin const&) = delete;
331 /* Both destructible and copy-constructible */
333 struct copy_assignment_mixin<T, true> {
334 copy_assignment_mixin() = default;
335 copy_assignment_mixin(copy_assignment_mixin&&) = default;
336 copy_assignment_mixin(copy_assignment_mixin const&) = default;
337 copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default;
338 inline copy_assignment_mixin&
339 operator=(copy_assignment_mixin const& other) noexcept(
340 std::is_nothrow_destructible<T>::value&&
341 std::is_nothrow_copy_constructible<T>::value) {
342 T* destruct_ptr = launder(reinterpret_cast<T*>(
343 reinterpret_cast<Replaceable<T>*>(this)->storage_));
345 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
346 T(*reinterpret_cast<Replaceable<T> const&>(other));
351 template <typename T>
352 struct is_constructible_from_replaceable
353 : std::integral_constant<
355 std::is_constructible<T, Replaceable<T>&>::value ||
356 std::is_constructible<T, Replaceable<T>&&>::value ||
357 std::is_constructible<T, const Replaceable<T>&>::value ||
358 std::is_constructible<T, const Replaceable<T>&&>::value> {};
360 template <typename T>
361 constexpr bool is_constructible_from_replaceable_v{
362 is_constructible_from_replaceable<T>::value};
364 template <typename T>
365 struct is_convertible_from_replaceable
366 : std::integral_constant<
368 std::is_convertible<Replaceable<T>&, T>::value ||
369 std::is_convertible<Replaceable<T>&&, T>::value ||
370 std::is_convertible<const Replaceable<T>&, T>::value ||
371 std::is_convertible<const Replaceable<T>&&, T>::value> {};
373 template <typename T>
374 constexpr bool is_convertible_from_replaceable_v{
375 is_convertible_from_replaceable<T>::value};
376 } // namespace replaceable_detail
378 // Type trait template to statically test whether a type is a specialization of
381 struct is_replaceable : std::false_type {};
384 struct is_replaceable<Replaceable<T>> : std::true_type {};
387 constexpr bool is_replaceable_v{is_replaceable<T>::value};
389 // Function to make a Replaceable with a type deduced from its input
391 constexpr Replaceable<std::decay_t<T>> make_replaceable(T&& t) {
392 return Replaceable<std::decay_t<T>>(std::forward<T>(t));
395 template <class T, class... Args>
396 constexpr Replaceable<T> make_replaceable(Args&&... args) {
397 return Replaceable<T>(in_place, std::forward<Args>(args)...);
400 template <class T, class U, class... Args>
401 constexpr Replaceable<T> make_replaceable(
402 std::initializer_list<U> il,
404 return Replaceable<T>(in_place, il, std::forward<Args>(args)...);
408 class alignas(T) Replaceable
409 : public replaceable_detail::dtor_mixin<
411 std::is_destructible<T>::value,
412 std::is_trivially_destructible<T>::value>,
413 public replaceable_detail::default_and_move_ctor_mixin<
415 std::is_default_constructible<T>::value,
416 std::is_move_constructible<T>::value>,
417 public replaceable_detail::
418 copy_ctor_mixin<T, std::is_copy_constructible<T>::value>,
419 public replaceable_detail::move_assignment_mixin<
421 std::is_destructible<T>::value &&
422 std::is_move_constructible<T>::value>,
423 public replaceable_detail::copy_assignment_mixin<
425 std::is_destructible<T>::value &&
426 std::is_copy_constructible<T>::value> {
427 using ctor_base = replaceable_detail::default_and_move_ctor_mixin<
429 std::is_constructible<T>::value,
430 std::is_move_constructible<T>::value>;
433 using value_type = T;
435 /* Rule-of-zero default- copy- and move- constructors. The ugly code to make
436 * these work are above, in namespace folly::replaceable_detail.
438 constexpr Replaceable() = default;
439 constexpr Replaceable(const Replaceable&) = default;
440 constexpr Replaceable(Replaceable&&) = default;
442 /* Rule-of-zero copy- and move- assignment operators. The ugly code to make
443 * these work are above, in namespace folly::replaceable_detail.
445 * Note - these destruct the `T` and then in-place construct a new one based
446 * on what is in the other replaceable; they do not invoke the assignment
449 Replaceable& operator=(const Replaceable&) = default;
450 Replaceable& operator=(Replaceable&&) = default;
452 /* Rule-of-zero destructor. The ugly code to make this work is above, in
453 * namespace folly::replaceable_detail.
455 ~Replaceable() = default;
458 * Constructors; these are modeled very closely on the definition of
459 * `std::optional` in C++17.
463 std::enable_if_t<std::is_constructible<T, Args&&...>::value, int> = 0>
464 constexpr explicit Replaceable(in_place_t, Args&&... args)
466 noexcept(std::is_nothrow_constructible<T, Args&&...>::value)
469 ::new (storage_) T(std::forward<Args>(args)...);
476 std::is_constructible<T, std::initializer_list<U>, Args&&...>::value,
478 constexpr explicit Replaceable(
480 std::initializer_list<U> il,
483 noexcept(std::is_nothrow_constructible<
485 std::initializer_list<U>,
489 ::new (storage_) T(il, std::forward<Args>(args)...);
495 std::is_constructible<T, U&&>::value &&
496 !std::is_same<std::decay_t<U>, in_place_t>::value &&
497 !std::is_same<Replaceable<T>, std::decay_t<U>>::value &&
498 std::is_convertible<U&&, T>::value,
500 constexpr /* implicit */ Replaceable(U&& other)
502 noexcept(std::is_nothrow_constructible<T, U&&>::value)
505 ::new (storage_) T(std::forward<U>(other));
511 std::is_constructible<T, U&&>::value &&
512 !std::is_same<std::decay_t<U>, in_place_t>::value &&
513 !std::is_same<Replaceable<T>, std::decay_t<U>>::value &&
514 !std::is_convertible<U&&, T>::value,
516 explicit constexpr Replaceable(U&& other)
518 noexcept(std::is_nothrow_constructible<T, U&&>::value)
521 ::new (storage_) T(std::forward<U>(other));
527 std::is_constructible<T, const U&>::value &&
528 !replaceable_detail::is_constructible_from_replaceable_v<T> &&
529 !replaceable_detail::is_convertible_from_replaceable_v<T> &&
530 std::is_convertible<const U&, T>::value,
532 /* implicit */ Replaceable(const Replaceable<U>& other)
534 noexcept(std::is_nothrow_constructible<T, U const&>::value)
537 ::new (storage_) T(*other);
543 std::is_constructible<T, const U&>::value &&
544 !replaceable_detail::is_constructible_from_replaceable_v<T> &&
545 !replaceable_detail::is_convertible_from_replaceable_v<T> &&
546 !std::is_convertible<const U&, T>::value,
548 explicit Replaceable(const Replaceable<U>& other)
550 noexcept(std::is_nothrow_constructible<T, U const&>::value)
553 ::new (storage_) T(*other);
559 std::is_constructible<T, U&&>::value &&
560 !replaceable_detail::is_constructible_from_replaceable_v<T> &&
561 !replaceable_detail::is_convertible_from_replaceable_v<T> &&
562 std::is_convertible<U&&, T>::value,
564 /* implicit */ Replaceable(Replaceable<U>&& other)
566 noexcept(std::is_nothrow_constructible<T, U&&>::value)
569 ::new (storage_) T(std::move(*other));
575 std::is_constructible<T, U&&>::value &&
576 !replaceable_detail::is_constructible_from_replaceable_v<T> &&
577 !replaceable_detail::is_convertible_from_replaceable_v<T> &&
578 !std::is_convertible<U&&, T>::value,
580 explicit Replaceable(Replaceable<U>&& other)
582 noexcept(std::is_nothrow_constructible<T, U&&>::value)
585 ::new (storage_) T(std::move(*other));
589 * `emplace` destructs the contained object and in-place constructs the
592 * The destructor must not throw (as usual). The constructor must not throw
593 * because that would violate the invariant that a `Replaceable<T>` always
594 * contains a T instance.
596 * As these methods are `noexcept` the program will be terminated if an
597 * exception is thrown. If you are encountering this issue you should look at
598 * using `Optional` instead.
600 template <class... Args>
601 T& emplace(Args&&... args) noexcept {
602 T* destruct_ptr = launder(reinterpret_cast<T*>(storage_));
604 return *::new (storage_) T(std::forward<Args>(args)...);
607 template <class U, class... Args>
608 T& emplace(std::initializer_list<U> il, Args&&... args) noexcept {
609 T* destruct_ptr = launder(reinterpret_cast<T*>(storage_));
611 return *::new (storage_) T(il, std::forward<Args>(args)...);
615 * `swap` just calls `swap(T&, T&)`.
617 * Should be `noexcept(std::is_nothrow_swappable<T>::value)` but we don't
618 * depend on C++17 features.
620 void swap(Replaceable& other) {
622 swap(*(*this), *other);
626 * Methods to access the contained object. Intended to be very unsurprising.
628 constexpr const T* operator->() const {
629 return launder(reinterpret_cast<T const*>(storage_));
632 constexpr T* operator->() {
633 return launder(reinterpret_cast<T*>(storage_));
636 constexpr const T& operator*() const & {
637 return *launder(reinterpret_cast<T const*>(storage_));
640 constexpr T& operator*() & {
641 return *launder(reinterpret_cast<T*>(storage_));
644 constexpr T&& operator*() && {
645 return std::move(*launder(reinterpret_cast<T*>(storage_)));
648 constexpr const T&& operator*() const && {
649 return std::move(*launder(reinterpret_cast<T const*>(storage_)));
653 friend struct replaceable_detail::dtor_mixin<
655 std::is_destructible<T>::value,
656 std::is_trivially_destructible<T>::value>;
657 friend struct replaceable_detail::default_and_move_ctor_mixin<
659 std::is_default_constructible<T>::value,
660 std::is_move_constructible<T>::value>;
661 friend struct replaceable_detail::
662 copy_ctor_mixin<T, std::is_constructible<T, T const&>::value>;
663 friend struct replaceable_detail::move_assignment_mixin<
665 std::is_destructible<T>::value && std::is_move_constructible<T>::value>;
666 friend struct replaceable_detail::copy_assignment_mixin<
668 std::is_destructible<T>::value && std::is_copy_constructible<T>::value>;
669 std::aligned_storage_t<sizeof(T), alignof(T)> storage_[1];
672 #if __cplusplus > 201402L
673 // C++17 allows us to define a deduction guide:
675 Replaceable(T)->Replaceable<T>;