1

std::ref/页面上的示例std::cref显示了使用std::ref/以一种看起来像通过引用获取参数的方式std::cref传递参数,而实际上它通过值获取所有参数。std::bindstd::bind

只看那个例子,我也可能对 的存在一无所知std::reference_wrapper,并且std::ref只是一个允许链接示例表现出的行为的函数。

这就是我std::ref问题标题和以下内容中的意思。

主要是为了好玩,我尝试实现std::ref自己,我想出了这个:

template<typename T>
struct ref_wrapper {
    ref_wrapper(T& t) : ref(t) {}
    T& ref;
    operator T&() const {
        return ref;
    }
};

template<typename T>
ref_wrapper<T> ref(T& t) {
    return ref_wrapper{t}; // Ooops
}

template<typename T>
ref_wrapper<const T> cref(const T& t) {
    return ref_wrapper{t}; // Ooops
}

在标记为// Ooops我错误地使用 CTAD行上,因为我正在使用-std=c++17. 通过更改ref_wrapperref_wrapper<T>ref_wrapper<const T>在这两种情况下更正了这一点。

然后我偷看了一眼/usr/include/c++/10.2.0/bits/refwrap.h

一方面,我看到 / 的实现与/ref的实现cref非常相似。std::refstd::cref

另一方面,我看到它std::reference_wrapper大约有 60 行长!里面有很多东西,包括noexcept, 宏, 复制 ctor, 复制operator=, get.

我认为其中大部分与std::reference_wrapper 仅用作从属std::ref无关,但有些东西可能是相关的,例如构造函数采用通用引用。

所以我的问题是:就我的瘦身尝试而言,工作的std::reference_wrapper必要条件和充分条件是什么?std::ref

我刚刚意识到std::reference_wrapper on cppreference有一个可能的实现(它比来自 GCC 的噪音小)。然而,即使在这里,也有一些我不明白的原因,例如operator()

4

1 回答 1

1

您正在谈论的逻辑完全在其内部实现std::bind。它需要的主要功能std::reference_wrapper是它可以“展开”(,您可以调用.get()它以检索底层引用)。当调用包装器(从 中返回的对象std::bind)被调用时,它会简单地检查其绑定的参数是否为 a std::reference_wrapper。如果是这样,它会调用.get()解包,然后将结果传递给绑定的可调用对象。

std::bind很复杂,因为它需要支持各种特殊情况,例如递归binding(这个功能现在被认为是设计错误),所以我不会尝试展示如何实现 full std::bind,而是展示一个bind足以满足关于 cppreference 的示例:

template <class Callable, class... Args>
auto bind(Callable&& callable, Args&&... args) {
    return [c=std::forward<Callable>(callable), ...a=std::forward<Args>(args)] () mutable {
        c(detail::unwrap_reference_wrapper(a)...);
    };
}

这个想法是bind保存自己的可调用副本和每个参数。如果参数是 a reference_wrapperreference_wrapper则将复制其本身,而不是所指对象。但是当实际调用调用包装器时,它会解开任何保存的引用包装器参数。执行此操作的代码很简单:

namespace detail {
    template <class T>
    T& unwrap_reference_wrapper(T& r) { return r; }

    template <class T>
    T& unwrap_reference_wrapper(reference_wrapper<T>& r) { return r.get(); }
}

也就是说,不是reference_wrappers 的参数被简单地传递,而reference_wrappers 经历第二个更专业的重载。

reference_wrapper本身只需要一个相关的构造函数和方法get()

template <class T>
class reference_wrapper {
  public:
    reference_wrapper(T& r) : p_(std::addressof(r)) {}
    T& get() const { return *p_; }

  private:
    T* p_;
};

和函数refcref容易实现。他们只是调用构造函数,推断出类型:

template <class T>
auto ref(T& r) { return reference_wrapper<T>(r); }

template <class T>
auto cref(T& r) { return reference_wrapper<const T>(r); }

您可以在 Coliru 上查看完整示例。

(如 cppreference 上所示, 的实际构造函数std::reference_wrapper很复杂,因为如果参数匹配右值引用而不是左值引用,它需要满足构造函数将禁用 SFINAE 的要求。就您的问题而言,它似乎没有必要进一步详细说明这个细节。)

于 2021-01-09T17:30:21.253 回答