如何存储通用引用
How to store universal references
我需要将通用引用存储在类中(我相信引用的值会比类更持久)。有规范的方法吗?
以下是我所想到的一个最简单的例子。这似乎有效,但我不确定我是否做对了。
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)
将不起作用,因为这些是r值引用,因此不允许binder(f, i)
。
你不能"存储通用引用",因为没有这样的东西,只有右值引用和左值引用。"通用引用"是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&
和X&
(或者在您总是用引用参数实例化类型的情况下,F
和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&&
binder(F&& f, X&& x)
运行良好。
F
和X
是引用类型(它们是模板实例化binder<F&&, X&&>
中给出的类型),因此f
和x
成为遵循通常的引用折叠规则的通用虔诚。
除此之外,代码看起来很好(只要你真的能确保引用的变量比绑定器活得更长)。
- 在函数内创建的对象的范围 - 如果在函数外部存储和访问引用,它们是否有效?
- 存储对(可能)临时对象的引用是否合法,只要引用不比对象存活?
- 在C++中,如果成员引用在其声明中初始化,为什么需要存储空间?
- 我们如何在指针中存储对对象的引用?
- 如何在拥有的C++对象中安全地存储对所有者的引用?
- C++ 在节点内存储对数据对象的引用,而不复制数据
- 标准库容器结构是否存储副本或引用?
- 如果结果存储在变量中forward_as_tuple然后在 std::apply 中使用之前丢失右值引用
- 是否可以将引用存储在标准::任何中
- 在模板中存储右值引用的危险
- 通过引用传递对象并将其存储为成员变量
- 每当传递lvalue时,每当通过rvalue传递时,将const引用存储
- 按名称存储和引用变量列表
- 在类中存储引用
- 我应该将引用存储在c++中的类中吗
- c++将对纯虚拟类的引用存储为类的成员
- 将对象引用存储在一个简单的容器中
- 您可以使用 boost::reference_wrapper 将引用存储在 STL 容器中吗?
- 我可以在不产生引用存储成本的情况下对变量进行别名吗
- 如何将对象作为引用存储在数组、列表或映射中