我有一个很大的std::vector<int> a
,但我想只在它的一个子集上工作。想法是创建仅包含所述子集的std::vector<reference_wrapper<int> > refa
(在mwe中,所有元素1&lt; a
&lt; 4)。然后,我想将refa
传递给期望std::vector<int>
或std::vector<int>&
作为参数的函数(因为我还想将它们与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
仅用于保持示例简单。
答案 0 :(得分:5)
我肯定会使用 iterators ,特别是考虑到你的问题(处理向量的子集):
template<class Iterator>
int bar(Iterator begin, Iterator end)
{
for (auto it = begin; it != end; ++it)
(*it)++;
return 0;
}
这样不仅可以从容器中抽象出来,而且还可以轻松地从经典的“begin”和“end”迭代器传递不同的迭代器来模拟特定的范围:
bar(a.begin() + 2, a.begin() + 4);
例如,使用上面的代码,您将访问1到4之间的元素(两者都被排除)。而here是现场的例子。
答案 1 :(得分:3)
最好的办法是制作功能栏和foobar模板功能,如下所示:
template <typename TContainer>
int bar(TContainer& va)
{
for(auto& vaa : va)
vaa++;
return 0;
}
如果不重新定义您的功能以接受“看起来像矢量的类型”,我认为没有办法达到您想要的效果。
答案 2 :(得分:1)
如果您不需要容器,请不要使用容器。使用可迭代范围。
容器既是可迭代范围,也是其内容的所有者。
在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<int>
或continguos_range<const int>
,并且可以隐式传递std::vector<int>
。
您还可以设置同等连续的std::vector
的子范围。
请注意,constiguous_range<int>
对应std::vector<int>&
,而contiguous_range<const int>
对应std::vector<int> const&
。