This verges on code review/best practices, but… is it reasonable to do this?
// A metafunction to map T -> T but std::reference_wrapper<T> -> T&:
template <typename T> struct RefWrapperToRef { using type = T; };
template <typename T> struct RefWrapperToRef<std::reference_wrapper<T>> { using type = T&; };
// A metafunction like std::remove_reference but std::reference_wrapper<T> -> T.
template <typename T> struct RemoveRefAndRefWrapper {
using type = std::remove_reference_t<typename RefWrapperToRef<T>::type>;
};
template <typename T>
struct S {
static_assert(not std::is_rvalue_reference_v<T>, "There may be a place for it, but it's dangerous.");
T x;
// Allow construction from a universal reference by forwarding.
template <typename U>
explicit S(U&& x) : x(std::forward<U>(x)) {
static_assert(std::is_same_v<std::remove_cv_t<typename RemoveRefAndRefWrapper<U>::type>,
std::remove_cv_t<typename RemoveRefAndRefWrapper<T>::type>>);
static_assert(not std::is_reference_v<T> or std::is_lvalue_reference_v<typename RefWrapperToRef<U>::type>);
}
};
template <typename T>
S(T) -> S<typename RefWrapperToRef<T>::type>;
That is, if you have int x
and call S(std::ref(x))
we’ll assume you want a S<int&>
. This seems elegant and simple and unambiguous yet slightly magical.