8

クラス内にユニバーサル参照を保存する必要があります(参照された値はクラスよりも長く存続すると確信しています)。そうする標準的な方法はありますか?

これは私が思いついたものの最小限の例です。うまくいくようですが、うまくいったかどうかはわかりません。

template <typename F, typename X>
struct binder
{
    template <typename G, typename Y>
    binder(G&& g, Y&& y) : f(std::forward<G>(g)), x(std::forward<Y>(y)) {}

    void operator()() { f(std::forward<X>(x)); }

    F&& f;
    X&& x;
};

template <typename F, typename X>
binder<F&&, X&&> bind(F&& f, X&& x)
{
    return binder<F&&, X&&>(std::forward<F>(f), std::forward<X>(x));
}

void test()
{
    int i = 1;
    const int j = 2;
    auto f = [](int){};

    bind(f, i)();   // X&& is int&
    bind(f, j)();   // X&& is const int&
    bind(f, 3)();   // X&& is int&&
}

私の推論は正しいですか、それとも微妙なバグにつながるのでしょうか? また、コンストラクタを記述するためのより良い (つまり、より簡潔な) 方法はありますか? binder(F&& f, X&& x)これらは右辺値参照であるため、機能しませんbinder(f, i)

4

2 に答える 2

7

右辺値参照と左辺値参照しかないため、「ユニバーサル参照を保存」することはできません。「ユニバーサル リファレンス」は、Scott Meyers が構文機能を説明するのに便利な用語ですが、型システムの一部ではありません

コードの特定の詳細を確認するには:

template <typename F, typename X>
binder<F&&, X&&> bind(F&& f, X&& x)

ここではbinder、テンプレート引数として参照型を使用してインスタンス化しているため、クラス定義では、メンバーを右辺値参照として宣言する必要はありません。メンバーは既に参照型であるためです(左辺値または によって推定される右辺値のいずれかbind)。これは&&、必要以上のトークンを常に取得していることを意味します。これは冗長であり、参照の崩壊により消えてしまいます。

binder関数によって常にインスタンス化される(したがって、常に参照型でインスタンス化される)ことが確実な場合はbind、次のように定義できます。

template <typename F, typename X>
struct binder
{
    binder(F g, X y) : f(std::forward<F>(g)), x(std::forward<X>(y)) {}

    void operator()() { f(std::forward<X>(x)); }

    F f;
    X x;
};

このバージョンでは、型FXは参照型であるため、使用するのは冗長でF&&ありX&&、それらは既に左辺値参照である (つまり&&は何もしない) か、右辺値参照である (&&この場合も は何もしない!) ためです。

または、そのままbinderにして次のように変更bindすることもできます。

template <typename F, typename X>
binder<F, X> bind(F&& f, X&& x)
{
    return binder<F, X>(std::forward<F>(f), std::forward<X>(x));
}

binderここで、左辺値参照型またはオブジェクト (つまり、非参照) 型のいずれかでインスタンス化してから、内部binderでメンバーを追加で宣言する&&ので、それらは左辺値参照型または右辺値参照型のいずれかになります。

さらに、考えてみれば、右辺値参照メンバーを格納する必要はありません。オブジェクトを左辺値参照で保存してもまったく問題ありません。重要なのは、オブジェクトを関数内の左辺値または右辺値として正しく転送operator()することだけです。したがって、クラス メンバーは単にF&and である可能性がありX&ます (または、参照引数を使用して型を常にインスタンス化する場合は、Fand X)

したがって、コードを次のように単純化します。

template <typename F, typename X>
struct binder
{
    binder(F& g, X& y) : f(g), x(y) { }

    void operator()() { f(std::forward<X>(x)); }

    F& f;
    X& x;
};

template <typename F, typename X>
binder<F, X> bind(F&& f, X&& x)
{
    return binder<F, X>(f, x);
}

このバージョンでは、テンプレート パラメーターで目的の型を保持し、F必要な唯一の場所である式Xで適切な型を使用します。std::forward<X>(x)

最後に、(折りたたまれた) 参照型だけでなく、推定された型の観点から考えると、より正確で役立つことがわかりました。

bind(f, i)();   // X is int&, X&& is int&
bind(f, j)();   // X is const int&, X&& is const int&
bind(f, 3)();   // X is int, X&& is int&&
于 2013-02-07T22:19:53.463 に答える
1

binder(F&& f, X&& x) 正常に動作します

FXは参照型(テンプレートのインスタンス化で指定された型binder<F&&, X&&>)であるため、通常の参照折りたたみルールに従って普遍的な畏敬の念になりますfx

それ以外は、コードは正常に見えます(参照される変数がバインダーよりも長持ちすることを実際に確認できる限り)。

于 2013-02-07T17:51:50.850 に答える