【问题标题】:Pass vector<reference_wrapper<int> > to vector<int>?将 vector<reference_wrapper<int> > 传递给 vector<int>?
【发布时间】:2014-02-11 16:44:51
【问题描述】:

我有一个很大的std::vector&lt;int&gt; a,但我只想处理它的一个子集。这个想法是创建一个仅包含上述子集的std::vector&lt;reference_wrapper&lt;int&gt; &gt; refa(在 mwe 中,所有元素 1arefa 传递给期望std::vector&lt;int&gt;std::vector&lt;int&gt;&amp; 作为参数的函数(因为我也想将它们与a 一起使用)。 a 可能很大,我想避免多次选择。

问题

如何正确地将refa 传递给函数?我想要的是 bar(refa)foobar(refa) 工作。

有没有更好的方法来解决问题,而不改变功能(太多)?

代码

#include <functional>
#include <iostream>
#include <vector>


int foo(int &a)
{
  a++;
  return 0;
}

int bar(std::vector<int> &va)
{
  for(auto &vaa : va)
    vaa++;

  return 0;
}

int foobar(std::vector<int> va)
{
  for(auto &vaa : va)
    vaa++;

  return 0;
}



int main()
{
  std::vector<int> a= {1, 2, 3, 4, 5};
  std::vector<std::reference_wrapper<int> > refa;

  //Fill refa
  for(auto &aa : a)
    {
      if(aa>1 && aa<4) refa.push_back(std::ref(aa));
    }


  //works
  // for(auto &aa : refa)
  //   aa++;

  //works
  //  bar(a);

  //works, a unchanged
  //  foobar(a);

  //works
  // for(auto &aa : refa)
  //   foo(aa);

  //works                         
  // for(int &aa : refa)
  //  foo(aa)

  // does not work
  // conversion from vector<reference_wrapper<int> > to vector<int>& or vector<int> required
  bar(refa);
  //  foobar(refa);


  for(auto &aa : a)
    std::cout << aa << std::endl;


  return 0;
}

注意 int 在这里仅用于保持示例简单。

【问题讨论】:

  • 也许得到一个迭代器范围?作为“视图”?
  • 1
  • 是的,它们可能不在连续范围内,我不想对它们进行排序。

标签: c++ c++11 vector stl reference-wrapper


【解决方案1】:

我肯定会使用迭代器,尤其是考虑到您的问题(处理向量的子集):

template<class Iterator>
int bar(Iterator begin, Iterator end)
{
    for (auto it = begin; it != end; ++it)
        (*it)++;
    return 0;
}

这样,您不仅可以从容器中抽象出来,还可以轻松地从经典的“开始”和“结束”迭代器中传递不同的迭代器来模拟特定的范围

bar(a.begin() + 2, a.begin() + 4);

例如,使用上面的代码,您将访问从 1 到 4 的元素(均不包括在内)。 here 就是一个活生生的例子。

【讨论】:

  • 但这只能在连续范围内有效,不是吗?在我的示例中确实如此,但在实际应用中不一定如此。
  • @Jost,这适用于每个符合 Iterator 概念的迭代器。要执行x.begin() + y,是的,您需要满足RandomAccessIterator 概念。
  • @Jost 不是;使用合适的迭代器适配器(例如boost::filter_iterator,天空就是极限。
【解决方案2】:

最好的办法是让函数 bar 和 foobar 模板函数,像这样:

template <typename TContainer> 
int bar(TContainer& va)
{
    for(auto& vaa : va)
        vaa++;

    return 0;
}

如果不重新定义你的函数来接受“看起来像矢量的类型”,我认为没有办法实现你想要的。

【讨论】:

  • 另一种方法是接受一对迭代器并使用例如boost::filter_iterator,但它仍然需要使用模板。
【解决方案3】:

如果您不需要容器,请不要使用容器。使用可迭代范围。

容器既是可迭代的范围,又是其内容的所有者。

vector 的情况下,它是一个连续的可迭代范围和内容的所有者。很可能你只需要知道你的实现代码中是否是一个随机访问的可迭代范围。

但是,处理任意连续的可迭代范围是有代价的,因为您必须将实现放在模板头文件中,或者进行昂贵的类型擦除。解决此问题的两种方法是使用您仅接受 vector 的子范围这一事实,或者使用您仅接受连续范围的子范围这一事实。

我自己喜欢连续范围的想法:

template<typename T>
struct contiguous_range {
  T* b; T* e;
  contiguous_range( contiguous_range const& ) = default;
  contiguous_range():b(nullptr), e(nullptr) {};
  contiguous_range& operator=( contiguous_range const& ) = default;
  std::size_t size() const { return e-b; }
  T* begin() const { return b; } // note, T*
  T* end() const { return e; } // note, T*
  template<typename U, typename=typename std::enable_if<
    sizeof(U)==sizeof(T)
    && std::is_convertible<U*, T*>::value
  >::type>
  contiguous_range( contiguous_range<U> const& o ):b(o.b), e(o.e) {};
  T& operator[]( std::size_t i ) const { return b[i]; }

  template<typename A>
  contiguous_range( std::vector<T, A> const& v ):b(v.data()), e(v.data()+v.size()) {}
  template<typename U, std::size_t N, typename=typename std::enable_if<
    sizeof(U)==sizeof(T)
    && std::is_convertible<U*, T*>::value
  >::type>
  contiguous_range( std::array<U, N> const& a ):b(a.data()), e(a.data()+a.size()) {}
  template<typename U, std::size_t N, typename=typename std::enable_if<
    sizeof(U)==sizeof(T)
    && std::is_convertible<U*, T*>::value
  >::type>
  contiguous_range( U(&a)[N] ):b(&a[0]), e((&a[0])+N) {}

  template<typename U, typename=typename std::enable_if<
    sizeof(U)==sizeof(T)
    && std::is_convertible<U*, T*>::value
  >::type>
  contiguous_range( U* b_, U* e_ ):b(b_), e(e_) {}
};

template<typename I>
auto contiguous_subrange( I b, I e )
-> contiguous_range<std::iterator_traits<I>::value_type>
{
  return {&*b, &*e};
}
template<typename C>
auto contiguous_subrange( C&& c, std::size_t start, std::size_t end )
-> decltype( contiguous_subrange( &c[start], &c[end] ) )
  { return ( contiguous_subrange( &c[start], &c[end] ) ) };

现在,我们的函数可以简单地接受 contiguous_range&lt;int&gt;continguos_range&lt;const int&gt;,并且可以隐式传递 std::vector&lt;int&gt;

您还可以设置同样连续的 std::vector 的子范围。

注意constiguous_range&lt;int&gt; 对应std::vector&lt;int&gt;&amp;contiguous_range&lt;const int&gt; 对应std::vector&lt;int&gt; const&amp;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    • 2023-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-15
    • 1970-01-01
    相关资源
    最近更新 更多