6

我一直试图从这里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.) 构造函数是一个模板函数,采用U与模板类 param 不同的类型 param() T。我已经看到一个类的成员函数是模板函数,并且取决于不同的类型参数,然后是模板类的类型参数,但我想不出它在这里是如何工作的。这里有一个相关的问题,但我无法将它与我的困惑联系起来。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<U>()将调用detail::FUN<T>(std::declval<int>(), 应该是由于deleted重载而导致的替换失败(假设std::declval<int>将被读取为对 int 的右值引用)。我在这里想念什么?

4

1 回答 1

5

在这种情况下,当您想要“转发引用”的行为时,您可以使用这种技术U&&,但同时限制可以绑定到它的内容。

下面提供的扣除T指南有助于扣除。通过选择已删除的重载并产生无效的表达式,detail::FUN<T>(std::declval<U>())可以确保在推断为右值引用时禁用构造函数。U如果U是另一个引用包装器,它也会被禁用,在这种情况下应该选择复制构造函数。

下面是几个有效和无效reference_wrapper的例子:

int i = 1; 

// OK: FUN<int>(std::declval<int&>()) is valid
std::reference_wrapper<int> rwi(i); 

// error: forming pointer to reference
std::reference_wrapper<int&> rwi2(i); 

// OK, uses deduction guide to find T = int
std::reference_wrapper rwi3(i);
std::reference_wrapper rwi4(++i);

// error: cannot deduce T, since there is no deduction guide for
// rvalue reference to T
std::reference_wrapper rwi5(std::move(i));

// error: substitution failure of FUN<int>(int&&)
std::reference_wrapper<int> rwi6(std::move(i));
std::reference_wrapper<int> rwi7(i++);
std::reference_wrapper<int> rwi8(i + i);
std::reference_wrapper<int> rwi9(2);

如您所见,对 deleted 的调用FUN<T>(T&&)仅在最后 4 种情况下起作用:当您明确指定T,但尝试从右值构造时。

于 2022-01-03T00:53:25.123 回答