右辺値参照と左辺値参照しかないため、「ユニバーサル参照を保存」することはできません。「ユニバーサル リファレンス」は、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;
};
このバージョンでは、型F
とX
は参照型であるため、使用するのは冗長で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&
ます (または、参照引数を使用して型を常にインスタンス化する場合は、F
and 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&&