标准::reference_wrapper<T> 容器中的用法

std::reference_wrapper<T> usage in a container

本文关键字:gt 用法 wrapper lt 标准 reference      更新时间:2023-10-16

如果可以的话,我会从代码中删除所有原始指针*,因为使用它们可能不是线程安全的,而且设计的意图也不明确(可选值、所有权等)。然而,有时不使用指针并不是那么容易。例如,我们倾向于在多态类型的容器中使用基类型的指针:

class A : noncopyable { ... };
class B : public A { ... };
std::vector<A*> v;
v.emplace_back(new B);
// temporary container for some operation
std::vector<A*> selected;
if(check())
selected.emplace_back(v.front());

关于以上代码,您能说些什么?谁是主人?它是不是共享所有权?这就是为什么我们可能应该对v:这样做

std::vector<std::unique_ptr<A>> v;
v.emplace_back(make_unique<B>());

现在很明显,v拥有这些对象,但我仍然不喜欢selected有一个原始指针,这使我的设计不直观。查看标准C++库,我认为只有一种类型可以完成这项工作——std::reference_wrapper:

std::vector<std::unique_ptr<A>> v;
v.emplace_back(make_unique<B>());
// temporary container for some operation
std::vector<std::reference_wrapper<A>> selected;
if(check())
selected.emplace_back(*v.front());

你觉得那个代码怎么样?这是一个好的做法吗?我知道std::ref()std::cref主要用于模板,但在这里我们似乎也可以使用它来明确说明我们的设计意图。我看到的唯一问题是,我必须用get()去引用std::reference_wrapper,并且里面没有operator*()operator->(),才能像在有unique_ptr的容器中那样拥有相同的接口。我应该自己写一些类似的东西吗?或者,在未来的C++版本中,可以为这样的用例扩展reference_wrapper?请分享您的反馈。

编辑:我更改了代码示例,以更好地显示意图。

您已经提供了一个看起来不错的解决方案。我知道问题是"你感觉如何?">

我个人的感觉是,一方面需要在安全性和明确性与另一方面代码的简单性之间保持某种平衡。看起来你的解决方案可能过于注重安全,过于简化。每当我使用包含"弱引用"的容器时,我都会使用原始指针来表示这些引用。的确,这可能会让对象的所有者不那么清楚,但它也有一些优点:你不必研究什么是"reference_wrapper",而且代码很清楚。如果只是临时使用它们(弱引用的容器),并且封装了这种用法,那么所有权问题应该是最小的。

但我想这只是个人喜好的问题。让我建议使用不同的类型来达到相同的目的。只要你能负担得起Boost。对于"强"引用(拥有资源),你可以使用Steve Watanabe的Type Erasure库。它不需要显式使用空闲存储内存,我想对于小型类型,它可以完全不用使用堆内存(使用小缓冲区优化)。它最近被Boost接受了,尽管我认为还没有发布。

对于弱引用,考虑使用Boost的"可选引用"。可选:

int i = 0;
boost::optional<int&> oi = i; // note: int&
i = 2;
assert(*oi == 2);

它和reference_wrapper具有相同的语义。

我认为称它们为shared_ptr的在逻辑上没有错。然而,看看std::weak_ptr:的定义

std::weak_ptr是一个智能指针,它包含一个非拥有("weak")对std::shared_ptr管理的对象的引用。一定是转换为std::shared_ptr以便访问引用的对象。

它可能是一个更好的候选者。至少当您在selected中摆弄指针时,您需要承担临时所有权。由于原始指针存储在共享指针中,因此使用弱指针会更安全。