我有一个像这样的容器类:
class A {
public:
// assuming all the other operators used below
// have been defined.
// ...
A operator()(const A& a) const {
A r(a.size());
for (int i = 0;i < a.size();++i) r[i] = data[a[i]];
return r;
}
private:
std::vector<int> data;
};
所以我可以这样做:
A a, b, c;
// ... initialize data here...
c = a(b); // I can index a by b
现在我想让索引容器a(b)
可分配,例如
a(b) = c;
例如,如果a
为{1, 2, 3, 4}
而b
为{0,2}
,c
为{0,0}
,则上述行应该给我{ {1}}。因为a = {0,2,0,4}
的{{1}}被a
{0,2}
编入{1,3}
,a
将c {0,0}
设为$scope.allItems = $filter('filter')($scope.allItems , { property: !$scope.currentItem.property});
。
怎么做?
答案 0 :(得分:1)
数组视图是T
:
template<class T>
struct array_view {
T* b=0;
T* e=0;
T* begin() const { return b; }
T* end() const { return e; }
std::size_t size() const { return end()-begin(); }
bool empty() const { return end()==begin(); }
T& operator[](std::size_t i)const { return begin()[i]; }
array_view( array_view const& ) = default;
array_view& operator=( array_view const& ) = default;
array_view() = default;
array_view( T* s, T* f ):b(s),e(f) {}
array_view( T* s, std::size_t n ):array_view(s, s+n) {}
};
template<class Src>
array_view< std::remove_reference_t<
decltype(*(std::declval<Src&>().data()))
> >
make_array_view( Src& src ) {
return {src.data(), src.size()};
}
(为了做得更好,请进行一般的“范围”升级。const
在上述情况下是指“更改查看范围”而不是内容 - 如果您想要const
内容,请一个array_view<const T>
。另一个改进是hvae构造函数执行make_array_view
所做的事情,也支持initializer_list
,rvalues和原始C数组。)
鉴于此,这是T
的数组视图的置换视图。
首先,置换是从有界的size_t集到另一组size_t的函数。
struct permutation {
std::function< std::size_t(std::size_t) > mapping;
std::size_t count = 0;
std::size_t size() const { return count; }
permutation( std::function< std::size_t(std::size_t) > m, std::size_t c):
mapping(std::move(m)),
count(c)
{}
std::size_t operator()( std::size_t i )const {
return mapping(i);
}
};
这不是最安全的,因为我们不检查输出范围是否合理。
工厂职能:
template<class T>
permutation make_permutation_from( T src ) {
auto size = src.size();
return {
[src = std::move(src)]( std::size_t in ) {
return src[in];
},
size
};
}
// optimization
permutation make_permutation_from( permutation src ) {
return src;
}
和一个组成两个排列。未检查size
字段的有效性。
// if they don't align, we are screwed, but...
permutation chain_permutation( permutation first, permutation second ) {
auto first_size = first.size();
return {
[first=std::move(first), second=std::move(second)](std::size_t i){
return second(first(i));
},
first_size
};
}
这会引导我们进入permuted view
,这是一个array_view
的视图,可以置换索引。
template<class T>
struct permuted_view {
array_view<T> source;
permutation permute;
std::size_t size() const { return permute.size(); }
T& operator[]( std::size_t i ) const {
return source[ permute(i) ];
}
template<class Src>
void assign_from( Src const& src ) {
if (src.size() != size()) exit(-1);
for (std::size_t i = 0; i < size(); ++i)
(*this)[i] = src[i];
}
void operator=( permuted_view const& src ) { assign_from(src); }
template<class U>
void operator=( U const& src ) { assign_from(src); }
template<class U,
std::enable_if_t< !std::is_integral<U>{}, int> =0
>
permuted_view<T> operator[]( U u )const {
return {
source,
chain_permutation( make_permutation_from(std::move(u)), permute )
};
}
};
现在,permuted_view<int>
是int
数组的排列。
请注意,permuted_view
不拥有任何内容。它只是指别人的存储空间。所有权是你必须为自己解决的问题。也许通过智能指针或其他方式。
用于此目的的高效库可能具有写时复制稀疏数组。要做到这一点,这是很多工作,或者你应该找到像Eigen这样的库。
您需要向begin
添加迭代器和end
/ permuted_view
。我将使迭代器存储指向视图和索引的指针,并在取消引用时使用operator[]
上的view
。
如果使用array_view<T>
迭代器的特化或子类将range_view<Iterator>
重构为T*
,则可以使用CRTP将range_view<Iterator>
重构为range_helper<Iterator, Derived>
。然后,对range_helper
和permuted_view
重用range_view
,为range_view
重用array_view
。但这有点偏离预订。
各种库,包括Rangesv3和boost和C ++ 17 std和C ++ 20 std :: experimental等,都可以编写这些类型,或者更容易编写这些类型。
答案 1 :(得分:1)
您无法直接使用A
类型执行此操作。您需要一个映射或引用回到对象的中间类型。作为一个简单的非最佳例子:
#include <functional>
#include <vector>
class A
{
public:
class Ref
{
private:
friend class A;
std::vector<std::reference_wrapper<int>> refs;
Ref( A & obj, A & idx )
{
refs.reserve( idx.data.size() );
auto val_it = obj.data.begin();
for( auto i : idx.data ) {
ref.emplace_back( std::ref( data[i] ) );
}
}
public:
Ref & operator=( const A & obj )
{
auto obj_it = obj.data.begin();
for( auto ref : refs ) {
ref.get() = *obj_it++;
}
return this;
}
};
// ...
Ref operator()( const A & idx )
{
return Ref( *this, idx );
}
private:
std::vector<int> data;
};
然后事情变得越来越繁琐,因为您希望能够在这些基于参考的视图和原始类型之间进行转换。
简单的方法是在实现时保留 const 运算符(注意我的非const ):即它仍然返回类型A
,这是有道理的。但您可能希望能够从A
构建新的A::Ref
,因此您需要一个类似的构造函数:
A::A( const Ref & r )
{
data.reserve( r.refs.size() );
auto r_it = r.refs.begin();
for( auto ref : r.refs ) {
data.push_back( ref.get() );
}
}
无论如何,这是一个让你开始的简单概念,也是一个可以玩的东西。