Skip to content

Commit c610727

Browse files
committed
Add a utility to simplify making a dynamic choice to move or copy.
Using it with emplacing methods relies on actually have a constructor that takes maybe_movable_ref<X>, which can be awkward. But there are cases where just calling construct() explicitly can be helpful.
1 parent 33bf18e commit c610727

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

include/swift/Basic/STLExtras.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,82 @@ auto transform(const std::optional<OptionalElement> &value,
793793
}
794794
return std::nullopt;
795795
}
796+
797+
/// A little wrapper that either wraps a `T &&` or a `const T &`.
798+
/// It allows you to defer the optimal decision about how to
799+
/// forward the value to runtime.
800+
template <class T>
801+
class maybe_movable_ref {
802+
/// Actually a T&& if movable is true.
803+
const T &ref;
804+
bool movable;
805+
806+
public:
807+
// The maybe_movable_ref wrapper itself is, basically, either an
808+
// r-value reference or an l-value reference. It is therefore
809+
// move-only so that code working with it has to properly
810+
// forward it around.
811+
maybe_movable_ref(maybe_movable_ref &&other) = default;
812+
maybe_movable_ref &operator=(maybe_movable_ref &&other) = default;
813+
814+
maybe_movable_ref(const maybe_movable_ref &other) = delete;
815+
maybe_movable_ref &operator=(const maybe_movable_ref &other) = delete;
816+
817+
/// Allow the wrapper to be statically constructed from an r-value
818+
/// reference in the movable state.
819+
maybe_movable_ref(T &&ref) : ref(ref), movable(true) {}
820+
821+
/// Allow the wrapper to be statically constructed from a
822+
/// const l-value reference in the non-movable state.
823+
maybe_movable_ref(const T &ref) : ref(ref), movable(false) {}
824+
825+
/// Don't allow the wrapper to be statically constructed from
826+
/// a non-const l-value reference without passing a flag
827+
/// dynamically.
828+
maybe_movable_ref(T &ref) = delete;
829+
830+
/// The fully-general constructor.
831+
maybe_movable_ref(T &ref, bool movable) : ref(ref), movable(movable) {}
832+
833+
/// Check dynamically whether the reference is movable.
834+
bool isMovable() const {
835+
return movable;
836+
}
837+
838+
/// Construct a T from the wrapped reference.
839+
T construct() && {
840+
if (isMovable()) {
841+
return T(move());
842+
} else {
843+
return T(ref);
844+
}
845+
}
846+
847+
/// Get access to the value, conservatively returning a const
848+
/// reference.
849+
const T &get() const {
850+
return ref;
851+
}
852+
853+
/// Get access to the value, dynamically aserting that it is movable.
854+
T &get_mutable() const {
855+
assert(isMovable());
856+
return const_cast<T&>(ref);
857+
}
858+
859+
/// Return an r-value reference to the value, dynamically asserting
860+
/// that it is movable.
861+
T &&move() {
862+
assert(isMovable());
863+
return static_cast<T&&>(const_cast<T&>(ref));
864+
}
865+
};
866+
867+
template <class T>
868+
maybe_movable_ref<T> move_if(T &ref, bool movable) {
869+
return maybe_movable_ref<T>(ref, movable);
870+
}
871+
796872
} // end namespace swift
797873

798874
#endif // SWIFT_BASIC_STLEXTRAS_H

0 commit comments

Comments
 (0)