簡體   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