【问题标题】:How should I convert a container to a container of reference_wrappers?我应该如何将容器转换为 reference_wrappers 的容器?
【发布时间】:2015-07-10 16:14:28
【问题描述】:

问题:

假设一个函数

void doFoo(const std::vector<std::reference_wrapper<DataT> >& data);

其中DataT 是一些保存一些数据的类型。我用std::vector作为容器类的典型例子。

您认为用std::vector&lt;DataT&gt; 调用上述函数最优雅的方式是什么?

背景:

在函数doFoo(...) 内部,我不想拥有数据的所有权。因此,我不想使用std::shared_ptrstd::unique_ptr。据我了解,正确的方法是使用参考。如果我只使用对std::vector&lt;DataT&gt; 的引用,我可能必须复制几个DataT 来为函数调用创建向量,以防我没有直接在std::vector&lt;DataT&gt; 中构造数据。因此,std::vector&lt;std::reference_wrapper&lt;DataT&gt; &gt; 似乎是最好的解决方案。

如果我只有一个 DataT 对象(我们称之为 data1),我可以通过调用 doFoo({data1}) 轻松地将其放入函数中,并且将隐式构造正确的向量。但如果我已经有一个std::vector&lt;DataT&gt; dataVec 并尝试调用doFoo(dataVec),它不会隐式转换为std::vector&lt;std::reference_wrapper&lt;DataT&gt; &gt;,因为类型完全不相关。

可能的解决方案:

  1. 在调用函数之前创建一个std::vector&lt;std::reference_wrapper&lt;DataT&gt; &gt;,并用对dataVec 中所有元素的引用填充它。
    缺点:我必须为每个函数调用重新打包。
  2. 重载doFoo(...) 以获取std::vector&lt;DataT&gt; 并在那里进行重新打包。
    缺点:如果我在很多地方使用这个概念,它会创建相当多的样板代码。此外,对我来说,调用额外的函数似乎有点多余,甚至可能令人困惑。
  3. 启用从std::vector&lt;DataT&gt;std::vector&lt;std::reference_wrapper&lt;DataT&gt; &gt; 的一些隐式转换。但我不知道这是否可能,以及是否会产生一些不好的后果。如果这个或类似的东西是可能的和安全的,我会更喜欢这个解决方案。

【问题讨论】:

  • “如果我只使用对 std::vector 的引用,我可能必须复制几个 DataT 来为函数调用创建向量”。不是这样 - 使用 const std::vector&lt;DataT&gt;&amp; data 不会对其绑定的向量的内部状态进行任何复制。
  • @Alejandro 我读了他的问题评论说他可能有一些不在向量中的 DataT 对象,他想对它们调用 doFoo 。他不想为了调用 doFoo 将它们复制到向量中
  • 抱歉不清楚:我的意思是当我已经有了数据,但它们没有打包到 std::vector&lt;DataT&gt; 中,或者即使我有一个该类型的对象但只想使用作为数据的子集,我必须创建一个新的std::vector&lt;DataT&gt; 并通过复制数据来填充它,然后才能真正调用该函数。 (谢谢,@pythonicmetaphor,我在看到你之前写了我的评论。)
  • doFoo 是否需要该签名?我认为std::vector&lt;DataT*&gt; 比使用reference_wrapper 更简单,DataT* 完美地表达了对您不拥有的东西的引用。如果更改 doFoo 是可能的,那么另一种选择是将其更改为采用一对迭代器,这样您就可以更灵活地传递给它,并且不必构造完全正确类型的容器。
  • 即我质疑你一开始就设置的前提:“假设一个函数” ...为什么假设一个看起来很奇怪的函数?

标签: c++ c++11 containers reference-wrapper


【解决方案1】:

我在这里看到了一些选项。

1.使用向量的迭代器构造函数。

std::vector<DataT> x;
doFoo({x.begin(), x.end()});

它应该从向量构造一个引用包装器的向量,因为迭代器无论如何都会引用对象。

2。使用std::vector&lt;DataT&gt; const &amp; 作为doFoo 参数并将您的数据移动到向量中。

如果您的数据在某处,您可以将它们移动到向量中而不是复制(如果移动 DataT 有意义的话)。

DataT d1, d2, d3;
std::vector<DataT> v { std::move(d1), std::move(d2), std::move(d3) };
dooFoo(v);

将所有权转移给向量(而不是函数)!

3.如果要处理的向量不止一个,一般对向量中的DataTs 进行操作。

如果您周围没有多个 DataT 对象,则需要将其复制到向量中,则不需要 reference_wrapper。

【讨论】:

  • 您的选项 2 似乎不是一个好主意,现在您已修改 d1d2d3 只是为了将它们传递给不会修改任何内容的函数(因为它只有对数据的 const 引用)。如果其他代码稍后需要使用这些变量怎么办?与std::vector&lt;DataT&gt; v{ std::move(d1), std::move(d2), std::move(d3) }; 相比,您的vector_emplace 函数有什么优势?即使你想要一个函数,它也不应该是递归的,它可以做到return { std::forward&lt;U&gt;(values)... };
  • @JonathanWakely 好吧,这个函数有点讨厌,但当然:所有权转移到了向量中,但这很明显。任何稍后需要数据的代码都必须对向量而不是 d1-3 进行操作。但它不是函数调用的临时对象,而是当前作用域中存在的向量。
猜你喜欢
  • 2020-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-04
  • 1970-01-01
  • 1970-01-01
  • 2019-09-05
  • 2021-05-16
相关资源
最近更新 更多