您不能“存储通用引用”,因为不存在这样的东西,只有右值引用和左值引用。 “通用参考”是 Scott Meyers 用来描述语法特征的方便术语,但它不是类型系统的一部分.
具体查看代码细节:
template <typename F, typename X>
binder<F&&, X&&> bind(F&& f, X&& x)
在这里你正在实例化binder
使用引用类型作为模板参数,因此在类定义中无需将成员声明为右值引用,因为它们已经are引用类型(左值或右值,由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
and X
是引用类型,所以使用是多余的F&&
and X&&
因为它们要么已经是左值引用(所以&&
什么都不做)或者它们是右值引用(所以&&
在这种情况下也什么也不做!)
或者,你可以保留binder
当你拥有它并改变它时bind
to:
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
您通过附加声明成员&&
所以它们要么是左值引用类型,要么是右值引用类型。
此外,如果您考虑一下,您不需要存储右值引用成员。通过左值引用存储对象是完全可以的,重要的是你forward它们正确地作为左值或右值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
and 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&&