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/Portability.h>
26 #include <folly/Traits.h>
27 #include <folly/Utility.h>
30 * An instance of `Replaceable<T>` wraps an instance of `T`.
32 * You access the inner `T` instance with `operator*` and `operator->` (as if
33 * it were a smart pointer).
35 * `Replaceable<T>` adds no indirection cost and performs no allocations.
37 * `Replaceable<T>` has the same size and alignment as `T`.
39 * You can replace the `T` within a `Replaceable<T>` using the `emplace` method
40 * (presuming that it is constructible and destructible without throwing
41 * exceptions). If the destructor or constructor you're using could throw an
42 * exception you should use `Optional<T>` instead, as it's not a logic error
43 * for that to be empty.
45 * Frequently Asked Questions
46 * ==========================
48 * Why does this need to be so complicated?
49 * ----------------------------------------
51 * If a `T` instance contains `const`-qualified member variables or reference
52 * member variables we can't safely replace a `T` instance by destructing it
53 * manually and using placement new. This is because compilers are permitted to
54 * assume that the `const` or reference members of a named, referenced, or
55 * pointed-to object do not change.
57 * For pointed-to objects in allocated storage you can use the pointer returned
58 * by placement new or use the `launder` function to get a pointer to the new
59 * object. Note that `launder` doesn't affect its argument, it's still
60 * undefined behaviour to use the original pointer. And none of this helps if
61 * the object is a local or a member variable because the destructor call will
62 * not have been laundered. In summary, this is the only way to use placement
63 * new that is both simple and safe:
67 * pT = ::new (pT) T(...);
70 * What are the other safe solutions to this problem?
71 * --------------------------------------------------
73 * * Ask the designer of `T` to de-`const` and -`reference` the members of `T`.
74 * - Makes `T` harder to reason about
75 * - Can reduce the performance of `T` methods
76 * - They can refuse to make the change
77 * * Put the `T` on the heap and use a raw/unique/shared pointer.
78 * - Adds a level of indirection, costing performance.
79 * - Harder to reason about your code as you need to check for nullptr.
80 * * Put the `T` in an `Optional`.
81 * - Harder to reason about your code as you need to check for None.
82 * * Pass the problem on, making the new code also not-replaceable
83 * - Contagion is not really a solution
85 * Are there downsides to this?
86 * ----------------------------
88 * There is a potential performance penalty after converting `T` to
89 * `Replaceable<T>` if you have non-`T`-member-function code which repeatedly
90 * examines the value of a `const` or `reference` data member of `T`, because
91 * the compiler now has to look at the value each time whereas previously it
92 * was permitted to load it once up-front and presume that it could never
98 * Don't store a reference to the `T` within a `Replaceable<T>` unless you can
99 * show that its lifetime does not cross an `emplace` call. For safety a
100 * reasonable rule is to always use `operator*()` to get a fresh temporary each
101 * time you need a `T&.
103 * If you store a pointer to the `T` within a `Replaceable<T>` you **must**
104 * launder it after each call to `emplace` before using it. Again you can
105 * reasonably choose to always use `operator->()` to get a fresh temporary each
106 * time you need a `T*.
108 * Thus far I haven't thought of a good reason to use `Replaceable<T>` or
109 * `Replaceable<T> const&` as a function parameter type.
111 * `Replaceable<T>&` can make sense to pass to a function that conditionally
112 * replaces the `T`, where `T` has `const` or reference member variables.
114 * The main use of `Replaceable<T>` is as a class member type or a local type
115 * in long-running functions.
117 * It's probably time to rethink your design choices if you end up with
118 * `Replaceable<Replaceable<T>>`, `Optional<Replaceable<T>>`,
119 * `Replaceable<Optional<T>>`, `unique_ptr<Replaceable<T>>` etc. except as a
120 * result of template expansion.
127 namespace replaceable_detail {
128 /* Mixin templates to give `replaceable<T>` the following properties:
130 * 1. Trivial destructor if `T` has a trivial destructor; user-provided
132 * 2. Move constructor if `T` has a move constructor; deleted otherwise
133 * 3. Move assignment operator if `T` has a move constructor; deleted
135 * 4. Copy constructor if `T` has a copy constructor; deleted otherwise
136 * 5. Copy assignment operator if `T` has a copy constructor; deleted
139 * Has to be done in this way because we can't `enable_if` them away
143 bool /* true iff destructible */,
144 bool /* true iff trivially destructible */>
147 /* Destructible and trivially destructible */
149 struct dtor_mixin<T, true, true> {};
151 /* Destructible and not trivially destructible */
153 struct dtor_mixin<T, true, false> {
154 dtor_mixin() = default;
155 dtor_mixin(dtor_mixin&&) = default;
156 dtor_mixin(dtor_mixin const&) = default;
157 dtor_mixin& operator=(dtor_mixin&&) = default;
158 dtor_mixin& operator=(dtor_mixin const&) = default;
159 ~dtor_mixin() noexcept(std::is_nothrow_destructible<T>::value) {
160 T* destruct_ptr = launder(reinterpret_cast<T*>(
161 reinterpret_cast<Replaceable<T>*>(this)->storage_));
166 /* Not destructible */
167 template <class T, bool A>
168 struct dtor_mixin<T, false, A> {
169 dtor_mixin() = default;
170 dtor_mixin(dtor_mixin&&) = default;
171 dtor_mixin(dtor_mixin const&) = default;
172 dtor_mixin& operator=(dtor_mixin&&) = default;
173 dtor_mixin& operator=(dtor_mixin const&) = default;
174 ~dtor_mixin() = delete;
179 bool /* true iff default constructible */,
180 bool /* true iff move constructible */>
181 struct default_and_move_ctor_mixin;
183 /* Not default-constructible and not move-constructible */
185 struct default_and_move_ctor_mixin<T, false, false> {
186 default_and_move_ctor_mixin() = delete;
187 default_and_move_ctor_mixin(default_and_move_ctor_mixin&&) = delete;
188 default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;
189 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =
191 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =
195 inline explicit default_and_move_ctor_mixin(int) {}
198 /* Default-constructible and move-constructible */
200 struct default_and_move_ctor_mixin<T, true, true> {
201 inline default_and_move_ctor_mixin() noexcept(
202 std::is_nothrow_constructible<T>::value) {
203 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T();
205 inline default_and_move_ctor_mixin(
206 default_and_move_ctor_mixin&&
207 other) noexcept(std::is_nothrow_constructible<T, T&&>::value) {
208 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
209 T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
211 default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;
212 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =
214 inline default_and_move_ctor_mixin& operator=(
215 default_and_move_ctor_mixin const&) = default;
218 inline explicit default_and_move_ctor_mixin(int) {}
221 /* Default-constructible and not move-constructible */
223 struct default_and_move_ctor_mixin<T, true, false> {
224 inline default_and_move_ctor_mixin() noexcept(
225 std::is_nothrow_constructible<T>::value) {
226 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_) T();
228 default_and_move_ctor_mixin(default_and_move_ctor_mixin&&) = delete;
229 default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;
230 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =
232 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =
236 inline explicit default_and_move_ctor_mixin(int) {}
239 /* Not default-constructible but is move-constructible */
241 struct default_and_move_ctor_mixin<T, false, true> {
242 default_and_move_ctor_mixin() = delete;
243 inline default_and_move_ctor_mixin(
244 default_and_move_ctor_mixin&&
245 other) noexcept(std::is_nothrow_constructible<T, T&&>::value) {
246 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
247 T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
249 default_and_move_ctor_mixin(default_and_move_ctor_mixin const&) = default;
250 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin&&) =
252 default_and_move_ctor_mixin& operator=(default_and_move_ctor_mixin const&) =
256 inline explicit default_and_move_ctor_mixin(int) {}
259 template <class T, bool /* true iff destructible and move constructible */>
260 struct move_assignment_mixin;
262 /* Not (destructible and move-constructible) */
264 struct move_assignment_mixin<T, false> {
265 move_assignment_mixin() = default;
266 move_assignment_mixin(move_assignment_mixin&&) = default;
267 move_assignment_mixin(move_assignment_mixin const&) = default;
268 move_assignment_mixin& operator=(move_assignment_mixin&&) = delete;
269 move_assignment_mixin& operator=(move_assignment_mixin const&) = default;
272 /* Both destructible and move-constructible */
274 struct move_assignment_mixin<T, true> {
275 move_assignment_mixin() = default;
276 move_assignment_mixin(move_assignment_mixin&&) = default;
277 move_assignment_mixin(move_assignment_mixin const&) = default;
278 inline move_assignment_mixin&
279 operator=(move_assignment_mixin&& other) noexcept(
280 std::is_nothrow_destructible<T>::value&&
281 std::is_nothrow_move_constructible<T>::value) {
282 T* destruct_ptr = launder(reinterpret_cast<T*>(
283 reinterpret_cast<Replaceable<T>*>(this)->storage_));
285 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
286 T(*std::move(reinterpret_cast<Replaceable<T>&>(other)));
289 move_assignment_mixin& operator=(move_assignment_mixin const&) = default;
292 template <class T, bool /* true iff copy constructible */>
293 struct copy_ctor_mixin;
295 /* Not copy-constructible */
297 struct copy_ctor_mixin<T, false> {
298 copy_ctor_mixin() = default;
299 copy_ctor_mixin(copy_ctor_mixin&&) = default;
300 copy_ctor_mixin(copy_ctor_mixin const&) = delete;
301 copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default;
302 copy_ctor_mixin& operator=(copy_ctor_mixin const&) = delete;
305 /* Copy-constructible */
307 struct copy_ctor_mixin<T, true> {
308 copy_ctor_mixin() = default;
309 inline copy_ctor_mixin(copy_ctor_mixin const& other) noexcept(
310 std::is_nothrow_constructible<T, T const&>::value) {
311 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
312 T(*reinterpret_cast<Replaceable<T> const&>(other));
314 copy_ctor_mixin(copy_ctor_mixin&&) = default;
315 copy_ctor_mixin& operator=(copy_ctor_mixin&&) = default;
316 copy_ctor_mixin& operator=(copy_ctor_mixin const&) = default;
319 template <class T, bool /* true iff destructible and copy constructible */>
320 struct copy_assignment_mixin;
322 /* Not (destructible and copy-constructible) */
324 struct copy_assignment_mixin<T, false> {
325 copy_assignment_mixin() = default;
326 copy_assignment_mixin(copy_assignment_mixin&&) = default;
327 copy_assignment_mixin(copy_assignment_mixin const&) = default;
328 copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default;
329 copy_assignment_mixin& operator=(copy_assignment_mixin const&) = delete;
332 /* Both destructible and copy-constructible */
334 struct copy_assignment_mixin<T, true> {
335 copy_assignment_mixin() = default;
336 copy_assignment_mixin(copy_assignment_mixin&&) = default;
337 copy_assignment_mixin(copy_assignment_mixin const&) = default;
338 copy_assignment_mixin& operator=(copy_assignment_mixin&&) = default;
339 inline copy_assignment_mixin&
340 operator=(copy_assignment_mixin const& other) noexcept(
341 std::is_nothrow_destructible<T>::value&&
342 std::is_nothrow_copy_constructible<T>::value) {
343 T* destruct_ptr = launder(reinterpret_cast<T*>(
344 reinterpret_cast<Replaceable<T>*>(this)->storage_));
346 ::new (reinterpret_cast<Replaceable<T>*>(this)->storage_)
347 T(*reinterpret_cast<Replaceable<T> const&>(other));
352 template <typename T>
353 struct is_constructible_from_replaceable
354 : std::integral_constant<
356 std::is_constructible<T, Replaceable<T>&>::value ||
357 std::is_constructible<T, Replaceable<T>&&>::value ||
358 std::is_constructible<T, const Replaceable<T>&>::value ||
359 std::is_constructible<T, const Replaceable<T>&&>::value> {};
361 template <typename T>
362 struct is_convertible_from_replaceable
363 : std::integral_constant<
365 std::is_convertible<Replaceable<T>&, T>::value ||
366 std::is_convertible<Replaceable<T>&&, T>::value ||
367 std::is_convertible<const Replaceable<T>&, T>::value ||
368 std::is_convertible<const Replaceable<T>&&, T>::value> {};
369 } // namespace replaceable_detail
371 // Type trait template to statically test whether a type is a specialization of
374 struct is_replaceable : std::false_type {};
377 struct is_replaceable<Replaceable<T>> : std::true_type {};
379 // Function to make a Replaceable with a type deduced from its input
381 constexpr Replaceable<std::decay_t<T>> make_replaceable(T&& t) {
382 return Replaceable<std::decay_t<T>>(std::forward<T>(t));
385 template <class T, class... Args>
386 constexpr Replaceable<T> make_replaceable(Args&&... args) {
387 return Replaceable<T>(in_place, std::forward<Args>(args)...);
390 template <class T, class U, class... Args>
391 constexpr Replaceable<T> make_replaceable(
392 std::initializer_list<U> il,
394 return Replaceable<T>(in_place, il, std::forward<Args>(args)...);
398 class alignas(T) Replaceable
399 : public replaceable_detail::dtor_mixin<
401 std::is_destructible<T>::value,
402 std::is_trivially_destructible<T>::value>,
403 public replaceable_detail::default_and_move_ctor_mixin<
405 std::is_default_constructible<T>::value,
406 std::is_move_constructible<T>::value>,
407 public replaceable_detail::
408 copy_ctor_mixin<T, std::is_copy_constructible<T>::value>,
409 public replaceable_detail::move_assignment_mixin<
411 std::is_destructible<T>::value &&
412 std::is_move_constructible<T>::value>,
413 public replaceable_detail::copy_assignment_mixin<
415 std::is_destructible<T>::value &&
416 std::is_copy_constructible<T>::value> {
417 using ctor_base = replaceable_detail::default_and_move_ctor_mixin<
419 std::is_constructible<T>::value,
420 std::is_move_constructible<T>::value>;
423 using value_type = T;
425 /* Rule-of-zero default- copy- and move- constructors. The ugly code to make
426 * these work are above, in namespace folly::replaceable_detail.
428 constexpr Replaceable() = default;
429 constexpr Replaceable(const Replaceable&) = default;
430 constexpr Replaceable(Replaceable&&) = default;
432 /* Rule-of-zero copy- and move- assignment operators. The ugly code to make
433 * these work are above, in namespace folly::replaceable_detail.
435 * Note - these destruct the `T` and then in-place construct a new one based
436 * on what is in the other replaceable; they do not invoke the assignment
439 Replaceable& operator=(const Replaceable&) = default;
440 Replaceable& operator=(Replaceable&&) = default;
442 /* Rule-of-zero destructor. The ugly code to make this work is above, in
443 * namespace folly::replaceable_detail.
445 ~Replaceable() = default;
448 * Constructors; these are modeled very closely on the definition of
449 * `std::optional` in C++17.
453 std::enable_if_t<std::is_constructible<T, Args&&...>::value, int> = 0>
454 FOLLY_CPP14_CONSTEXPR explicit Replaceable(in_place_t, Args&&... args)
456 noexcept(std::is_nothrow_constructible<T, Args&&...>::value)
459 ::new (storage_) T(std::forward<Args>(args)...);
466 std::is_constructible<T, std::initializer_list<U>, Args&&...>::value,
468 FOLLY_CPP14_CONSTEXPR explicit Replaceable(
470 std::initializer_list<U> il,
473 noexcept(std::is_nothrow_constructible<
475 std::initializer_list<U>,
479 ::new (storage_) T(il, std::forward<Args>(args)...);
485 std::is_constructible<T, U&&>::value &&
486 !std::is_same<std::decay_t<U>, in_place_t>::value &&
487 !std::is_same<Replaceable<T>, std::decay_t<U>>::value &&
488 std::is_convertible<U&&, T>::value,
490 FOLLY_CPP14_CONSTEXPR /* implicit */ Replaceable(U&& other)
492 noexcept(std::is_nothrow_constructible<T, U&&>::value)
495 ::new (storage_) T(std::forward<U>(other));
501 std::is_constructible<T, U&&>::value &&
502 !std::is_same<std::decay_t<U>, in_place_t>::value &&
503 !std::is_same<Replaceable<T>, std::decay_t<U>>::value &&
504 !std::is_convertible<U&&, T>::value,
506 FOLLY_CPP14_CONSTEXPR explicit Replaceable(U&& other)
508 noexcept(std::is_nothrow_constructible<T, U&&>::value)
511 ::new (storage_) T(std::forward<U>(other));
517 std::is_constructible<T, const U&>::value &&
518 !replaceable_detail::is_constructible_from_replaceable<
520 !replaceable_detail::is_convertible_from_replaceable<T>::value &&
521 std::is_convertible<const U&, T>::value,
523 /* implicit */ Replaceable(const Replaceable<U>& other)
525 noexcept(std::is_nothrow_constructible<T, U const&>::value)
528 ::new (storage_) T(*other);
534 std::is_constructible<T, const U&>::value &&
535 !replaceable_detail::is_constructible_from_replaceable<
537 !replaceable_detail::is_convertible_from_replaceable<T>::value &&
538 !std::is_convertible<const U&, T>::value,
540 explicit Replaceable(const Replaceable<U>& other)
542 noexcept(std::is_nothrow_constructible<T, U const&>::value)
545 ::new (storage_) T(*other);
551 std::is_constructible<T, U&&>::value &&
552 !replaceable_detail::is_constructible_from_replaceable<
554 !replaceable_detail::is_convertible_from_replaceable<T>::value &&
555 std::is_convertible<U&&, T>::value,
557 /* implicit */ Replaceable(Replaceable<U>&& other)
559 noexcept(std::is_nothrow_constructible<T, U&&>::value)
562 ::new (storage_) T(std::move(*other));
568 std::is_constructible<T, U&&>::value &&
569 !replaceable_detail::is_constructible_from_replaceable<
571 !replaceable_detail::is_convertible_from_replaceable<T>::value &&
572 !std::is_convertible<U&&, T>::value,
574 explicit Replaceable(Replaceable<U>&& other)
576 noexcept(std::is_nothrow_constructible<T, U&&>::value)
579 ::new (storage_) T(std::move(*other));
583 * `emplace` destructs the contained object and in-place constructs the
586 * The destructor must not throw (as usual). The constructor must not throw
587 * because that would violate the invariant that a `Replaceable<T>` always
588 * contains a T instance.
590 * As these methods are `noexcept` the program will be terminated if an
591 * exception is thrown. If you are encountering this issue you should look at
592 * using `Optional` instead.
594 template <class... Args>
595 T& emplace(Args&&... args) noexcept {
596 T* destruct_ptr = launder(reinterpret_cast<T*>(storage_));
598 return *::new (storage_) T(std::forward<Args>(args)...);
601 template <class U, class... Args>
602 T& emplace(std::initializer_list<U> il, Args&&... args) noexcept {
603 T* destruct_ptr = launder(reinterpret_cast<T*>(storage_));
605 return *::new (storage_) T(il, std::forward<Args>(args)...);
609 * `swap` just calls `swap(T&, T&)`.
611 * Should be `noexcept(std::is_nothrow_swappable<T>::value)` but we don't
612 * depend on C++17 features.
614 void swap(Replaceable& other) {
616 swap(*(*this), *other);
620 * Methods to access the contained object. Intended to be very unsurprising.
622 constexpr const T* operator->() const {
623 return launder(reinterpret_cast<T const*>(storage_));
626 FOLLY_CPP14_CONSTEXPR T* operator->() {
627 return launder(reinterpret_cast<T*>(storage_));
630 constexpr const T& operator*() const & {
631 return *launder(reinterpret_cast<T const*>(storage_));
634 FOLLY_CPP14_CONSTEXPR T& operator*() & {
635 return *launder(reinterpret_cast<T*>(storage_));
638 FOLLY_CPP14_CONSTEXPR T&& operator*() && {
639 return std::move(*launder(reinterpret_cast<T*>(storage_)));
642 constexpr const T&& operator*() const && {
643 return std::move(*launder(reinterpret_cast<T const*>(storage_)));
647 friend struct replaceable_detail::dtor_mixin<
649 std::is_destructible<T>::value,
650 std::is_trivially_destructible<T>::value>;
651 friend struct replaceable_detail::default_and_move_ctor_mixin<
653 std::is_default_constructible<T>::value,
654 std::is_move_constructible<T>::value>;
655 friend struct replaceable_detail::
656 copy_ctor_mixin<T, std::is_constructible<T, T const&>::value>;
657 friend struct replaceable_detail::move_assignment_mixin<
659 std::is_destructible<T>::value && std::is_move_constructible<T>::value>;
660 friend struct replaceable_detail::copy_assignment_mixin<
662 std::is_destructible<T>::value && std::is_copy_constructible<T>::value>;
663 std::aligned_storage_t<sizeof(T), alignof(T)> storage_[1];
666 #if __cplusplus > 201402L
667 // C++17 allows us to define a deduction guide:
669 Replaceable(T)->Replaceable<T>;