繁体   English   中英

为了使 std::ref 工作的唯一目的, std::reference_wrapper 实现的关键是什么?

[英]What is the crux of std::reference_wrapper implementation for the only purpose of makin std::ref work?

std::ref / std::cref页面上的示例显示了使用std::ref / std::cref将 arguments 传递给std::bind的方式看起来像std::bind正在使用 arguments参考,而实际上它是按价值取值的。

只看那个例子,我也可能不知道std::reference_wrapper的存在,而std::ref只是一个 function ,它允许链接示例展示的行为。

这就是我所说的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 Ooops 的行中,我错误地使用了CTAD ,因为我正在使用-std=c++17进行编译。 通过在这两种情况下将ref_wrapper更改为ref_wrapper<T>ref_wrapper<const T>来纠正这个问题。

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

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

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

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

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

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

您正在谈论的逻辑完全在std::bind本身内实现。 它需要std::reference_wrapper的主要功能是它可以“解包”(,您可以在其上调用.get()以检索底层引用)。 当调用包装器(std::bind返回的 object )被调用时,它只检查其绑定的任何 arguments 是否是std::reference_wrapper 如果是这样,它会调用.get()来解包,然后将结果传递给绑定的可调用对象。

std::bind很复杂,因为它需要支持各种特殊情况,例如递归bind (这个特性现在被认为是设计错误),所以我没有试图展示如何实现完整的std::bind ,而是将显示一个足以满足 cppreference 示例的自定义bind模板:

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保存它自己的可调用对象和每个参数的副本 如果参数是reference_wrapper ,则将复制reference_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_wrapper的 arguments 被简单地通过,而reference_wrapper的 go 通过第二个更专业的重载。

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 的要求。出于以下目的你的问题,似乎没有必要进一步详细说明这个细节。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM