【问题标题】:Implementing access to vector of stucts using a vector as indices使用向量作为索引实现对结构向量的访问
【发布时间】:2015-09-08 07:51:19
【问题描述】:

一些高级语言具有此功能,我正在尝试在 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;

这些事情可能吗?

【问题讨论】:

  • 你想构造iResultdResult这两个向量吗?以后要保留vinds 吗?如果是这样,您是否担心这两组向量稍后会发散?
  • 请不要在 C++ 中使用 typedef struct
  • v 应该保留,我可能不在乎 inds 会发生什么。 dResult 和 iResult 应始终包含 v 成员的子集,并且可能不会被修改。但是,我可能会寻找 dResult 和 iResult 的最大值或最小值。

标签: c++ vector operator-overloading


【解决方案1】:

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

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();

Live demo.

【讨论】:

    【解决方案2】:

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

    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';
    

    Live demo

    【讨论】:

    • +1,虽然既然你在这...可用性...
    • @David 我开始用迭代器作为参数编写它,但这使得第二个重载编写起来有点混乱。 使用 lambda 进行映射 ... 不确定您的意思。你是说用 lambda 替换 mem_fn 吗?如果是这样,如何使它变得更好?
    • @David 好的,我想我明白你的意思了。你说最好用你用每个元素调用的任意可调用对象替换指向数据成员的指针。同意,这确实使函数更灵活,但它也使select_mem 用词不当:) 在这种情况下,它可能应该称为map 或类似的名称。
    • 是的,用 Dietmar Kühl 的话来说,提供一个 generic 属性映射,让调用看起来像 select(v, std::mem_fn(&amp;s::x)),以便不同的用户可以通过一些操作执行 select(v, [](auto x){...})可能不只是调用 member 函数
    【解决方案3】:
    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);
    

    【讨论】:

      猜你喜欢
      • 2019-10-08
      • 2010-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多