使用向量作为索引实现对结构向量的访问

Implementing access to vector of stucts using a vector as indices

本文关键字:向量 结构 实现 访问 索引      更新时间:2023-10-16

一些高级语言具有此功能,我正在尝试用C++实现。我不确定某个地方是否有图书馆已经这样做了,但如果有,那会给我省去很多麻烦。不管怎样,以下是我正在努力实现的目标:

假设我有一个结构向量,它有一个double和一个int作为成员,还有另一个int向量,它表示我想要保留的结构向量中元素的索引。

typedef struct
{
double x;
int y;
}s;
std::vector<s> v;
std::vector<int> inds;

有没有一种方法可以用类似的方式使用索引向量来实现访问结构中的元素,或者在其他地方已经实现了?

std::vector<double> dResult = v[inds].x;
std::vector<int> iResult = v[inds].y;

能够以这种方式访问所有元素也是很好的:

std::vector<double> d = v.x;

这些事情可能吗?

  1. 不能将该语法与std::vector的现有定义一起使用
  2. 不能创建提供该语法的全局运算符重载函数,因为operator[]()只能作为成员函数重载
  3. 您可以创建一个提供该功能的非成员函数,但不使用该语法

template <typename T1, typename T2>
std::vector<T2> getElements(std::vector<T1> const& vec,
                            std::vector<int> const& indices,
                            T2 (T1::*member))
{
   std::vector<T2> ret;
   for ( auto index : indices )
   {
      ret.push_back(vec[index].*member);
   }
   return ret;
}

然后将其用作:

std::vector<s> v;
std::vector<int> inds;
std::vector<double> dResult = getElements(v, inds, &s::x);

不存在这样的内置功能,我也不知道有任何现有的库解决方案。但是,为您编写几个函数模板并不太难。

template<typename Container, typename T, typename M>
std::vector<M> select_mem(Container const& c, M T::* mem)
{
    std::vector<M> result;
    result.reserve(c.size());
    std::transform(c.begin(), c.end(), std::back_inserter(result),
                   std::mem_fn(mem));
    return result;
}

上面的模板引用了一个容器和一个指向数据成员的指针。然后,它将该数据成员从输入容器中的每个元素复制到输出vector中。

template<typename Container, typename Indices, typename T, typename M>
std::vector<M> select_mem(Container const& c, Indices const& ind, M T::* mem)
{
    std::vector<M> result;
    result.reserve(ind.size());
    std::transform(ind.begin(), ind.end(), std::back_inserter(result),
                   [&c, mem](typename Indices::value_type const& i) {
                       return std::mem_fn(mem)(c[i]);
                   });
    return result;
}

这是上一个模板的扩展,它也接受一个索引容器,并且只复制输入容器中指示索引处的数据成员。

按如下方式使用它们:

std::vector<s> v {{10, 1}, {20, 2}, {30, 3}, {40, 4}};
for(auto const& x : select_mem(v, &s::x)) {
    std::cout << x << ' ';
}
std::cout << 'n';
std::vector<int> indices{1,2};
for(auto const& x : select_mem(v, indices, &s::x)) {
    std::cout << x << ' ';
}
std::cout << 'n';

实时演示

要使语法接近您所需的语法,您可以创建一个类来包装结构的向量,并为结构中的每个成员变量提供成员函数:

class VecS {
  const std::vector<s>&   v;
  const std::vector<int>* inds;
  template<typename R>
  std::vector<R> impl(R s::* pm) const {
    if (inds) {
      std::vector<R> ret(inds->size());
      auto get_at_index = [this, pm](int index){ return v[index].*pm; };
      std::transform(inds->begin(), inds->end(), ret.begin(), get_at_index);
      return ret;
    }
    std::vector<R> ret(v.size());
    std::transform(v.begin(), v.end(), ret.begin(), std::mem_fn(pm));
    return ret;    
  }
public:
  VecS(const std::vector<s>& v) : v(v), inds(nullptr) {}
  VecS(const std::vector<s>& v, const std::vector<int>& inds) : v(v), inds(&inds) {}
  std::vector<double> x() const { return impl(&s::x); }
  std::vector<int>    y() const { return impl(&s::y); }
};

如果你愿意滥用operator[],你可以更进一步,添加一些类似的内容:

VecS operator[](const std::vector<int>& inds) { return VecS(v, inds); }

然后你可以写:

auto vs = VecS(v);
auto dResult = vs[inds].x(); 
auto iResult = vs[inds].y();

当然还有:

auto d = vs.x();

现场演示。