[英]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.