我一直在尝试理解std::reference_wrapper
的实现,从这里开始,其实现如下:
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;
};
尽管std::reference_wrapper的实现已经在这里和这里讨论过,但都没有讨论我困惑的构造函数实现。我的困惑是: 1.)构造函数是一个模板函数,接受与模板类参数
T
不同的类型参数U
。我见过类的成员函数是模板函数,并依赖于不同于模板类的类型参数,但我无法想象它在这里是如何工作的。有一个相关的问题here,但我无法将其与我的困惑联系起来。
2.)我看到构造函数中的第二个类型参数进一步用于sfinae出某些内容,但我不明白如何评估detail::FUN<T>(std::declval<U>())
。有人能解释一下吗?
编辑:这是从microsoft.docs添加的示例。示例的一部分如下:
int i = 1;
std::reference_wrapper<int> rwi(i); // A.1
rwi.get() = -1;
std::cout << "i = " << i << std::endl; //Prints -1
使用reference_wrapper实现后,从A.1开始,reference_wrapper的构造函数如何调用?假设会调用
detail::FUN<T>(std::declval<int>()
,因为重载中有一个deleted
版本(假设std::declval<int>
将被读取为对int的右值引用),所以应该是一次替换失败。我在这里错过了什么?
std::reference_wrapper
,而不会对重载决议产生负面影响。构造函数的这个规定是缺陷报告的结果。 - user17732522i
),则它将调用detail::FUN<T>(std::declval<int&>())
。在这种情况下,declval
返回一个左值引用,并选择非删除重载。 - user17732522