1

std::ref/のページの例は、std::crefstd::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
}

でコンパイルしていたため、誤ってCTAD// Ooopsを使用したとマークされた行のどこか。2 つのケースでとに変更することで、これが修正されます。-std=c++17ref_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、マクロ、copy ctor、copyなど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) が呼び出されると、バインドされた引数のいずれかが であるかどうかを単純にチェックしますstd::reference_wrapper。その場合、それ.get()をアンラップするために呼び出し、バインドされた callable に結果を渡します。

std::bind再帰 ing などのさまざまな特殊なケースをサポートする必要があるため複雑です(この機能は現在、設計ミスと見なされていますbind)。そのため、完全な を実装する方法を示す代わりに、 cppreference の例:std::bindbind

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)...);
    };
}

アイデアは、callable と各引数のbind独自のコピーを保存することです。引数が の場合、参照先ではなく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 は 2 番目のより特化したオーバーロードを通過します。

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無効になるという要件を満たす必要があるためです。質問の目的のために、それはこの詳細をさらに詳しく説明する必要はないようです。)

于 2021-01-09T17:30:21.253 に答える