I have been trying to understand the implementation of std::reference_wrapper, from here, which is as follows:
namespace detail {
template <class T> constexpr T& FUN(T& t) noexcept { return t; }
template <class T> void FUN(T&&) = delete;
}
template <class T>
class reference_wrapper {
public:
// types
typedef T type;
// construct/copy/destroy
template <class U, class = decltype(
detail::FUN<T>(std::declval<U>()),
std::enable_if_t<!std::is_same_v<reference_wrapper, std::remove_cvref_t<U>>>()
)>
constexpr reference_wrapper(U&& u) noexcept(noexcept(detail::FUN<T>(std::forward<U>(u))))
: _ptr(std::addressof(detail::FUN<T>(std::forward<U>(u)))) {}
reference_wrapper(const reference_wrapper&) noexcept = default;
// assignment
reference_wrapper& operator=(const reference_wrapper& x) noexcept = default;
// access
constexpr operator T& () const noexcept { return *_ptr; }
constexpr T& get() const noexcept { return *_ptr; }
template< class... ArgTypes >
constexpr std::invoke_result_t<T&, ArgTypes...>
operator() ( ArgTypes&&... args ) const {
return std::invoke(get(), std::forward<ArgTypes>(args)...);
}
private:
T* _ptr;
};
Although the implementation of std::reference_wrapper has been discussed hereand here,but none of it discusses the constructor implementation which i am confused about. My confusions are :
1.) Constructor is a template function , taking a type param (U) different from the template class param T. I have seen member functions of a class being template functions and depending on different type params then the type param of the template class, but i can't think how it is works here. There is a related question here, but i am not able to relate it with my confusion.
2.)I see the second type parameter in the constructor is further used to sfinae out something, but i did not understand howdetail::FUN<T>(std::declval<U>()) is evaluated.
Can someone please explain ?
Edit: This is an example added from microsoft.docs. A snippet of the example is:
int i = 1;
std::reference_wrapper<int> rwi(i); // A.1
rwi.get() = -1;
std::cout << "i = " << i << std::endl; //Prints -1
With the implementation of reference_wrapper , and from A.1, how is the constructor of the reference_wrapper called ? Assuming that detail::FUN<T>(std::declval<U>() will be called with detail::FUN<T>(std::declval<int>(), should be a substitution failure because of the deleted overload(Assuming std::declval<int> will be read as an rvalue reference to int). What am i missing here ?
It's a technique you can use when you want the behaviour of the "forwarding reference",
U&&in this case, but at the same time restrict what can bind to it.Deduction of
Tis aided by the deduction guide provided below. Thedetail::FUN<T>(std::declval<U>())is there to ensure that the constructor is disabled whenUis deduced to be an rvalue reference, by selecting the deleted overload and producing an invalid expression. It is also disabled if theUis another reference wrapper, in which case the copy constructor should be selected.Here are a few examples of valid and invalid
reference_wrappers:As you can see, the call to the deleted
FUN<T>(T&&)only comes into play in the last 4 cases: when you explicitly specifyT, but attempt to construct from an rvalue.