[英]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_;
};
ref
和cref
函数很容易实现。 他们只是调用构造函数,推断出类型:
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.