0

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.

https://godbolt.org/z/r7KqGKnsf

Ben
  • 9,184
  • 1
  • 43
  • 56
  • I'm not 100% sure what the question is. – Ted Lyngmo Dec 23 '21 at 03:06
  • 2
    I don't see anything wrong with it. If someone is using `std::ref` in the call site, then they are explicitly documenting that they want to pass the object as a reference. – NathanOliver Dec 23 '21 at 03:10
  • Right. I guess as a question I could ask “Does if violate the principle of least surprise that it doesn’t produce `S>` and “decays” to S`?” Because I’ve never seen this pattern but it seems fairly useful and elegant. – Ben Dec 23 '21 at 03:31
  • This specific example is broken because if `T` is an lvalue reference `int&`, it can't be initialized by `std::move(x)` of type `int&&`. Also, members of reference type are usually a bad idea, and you should just have a reference_wrapper member anyways. – Artyer Dec 23 '21 at 07:25
  • You are right @Artyer – Ben Dec 23 '21 at 14:10

0 Answers0