лучшая практика для выбора и изменения записей во вложенных векторах - PullRequest
0 голосов
/ 12 февраля 2020

Я ищу эффективный способ решения следующей (предположительно простой) проблемы:

  • У меня есть вектор типа A.
  • Класс A содержит вектор типа B.
  • Я не могу изменить класс A или дизайн вектора класса A (например, я не могу сделать его вектором указателей на A).

Я хочу иметь вектор класса B, который содержит все записи B, содержащиеся в векторе A со специальным атрибутом.

Я думаю, что это немного сложно прочитать о проблеме, но, вероятно, легче понять, если вы увидите пример:

#include <vector>
#include <iostream>
using namespace std;

struct B {
  int n;
  double val;
};

struct A {
  vector<B> v;
};

int main() {
  // generate some dummy data:
  A a1;
  a1.v.push_back(B{1, 1.0});
  a1.v.push_back(B{2, 2.0});
  a1.v.push_back(B{1, 3.5});

  A a2;
  a1.v.push_back(B{2, 2.0});
  a1.v.push_back(B{3, 1.0});
  a1.v.push_back(B{1, 2.5});

  // this is my initial situation: a vector of type A
  vector<A> va;
  va.push_back(a1);
  va.push_back(a2);

  // what I want to get is a vector of type B with all B values whose n == 1
  vector<B> vb;

  // possible solution to get all elements of B
  for(auto &any : va){
    for(auto &b : any.v){
      if(b.n == 1){
        vb.push_back(b);
      }
    }
  }

  for(const auto &any:vb){
    cout << any.val << endl;
  }

  return 0;
}

Вопросы:

Я хотел бы получить вектор класса B, не копируя каждый элемент. Позже мне нужно изменить элементы в векторе B, и они также должны быть изменены в векторе A (то есть, если я изменю vb[0].val = 100;, запись в va[0].v[0] также должна быть 100;

Я думал об использовании указателей для этого, но я не уверен, что это хорошая идея, поскольку элементы вектора va размещаются в стеке, а не в куче.

** Редактировать: * *

Я могу гарантировать, что, как только мне понадобится вектор B, я никоим образом не изменю вектор A.

1 Ответ

2 голосов
/ 12 февраля 2020

Наличие va в стеке не обязательно является проблемой, если вы перемещаете va в его новое местоположение. В этом случае ваш вектор «перемещено в» будет внутренне указывать на память, выделенную вашим вектором «перемещен из» va (и исходный va станет пустым).

В чем проблема, если вы добавляете новые B s к A после того, как уже создали указатели на B s. Если у вектора A v недостаточно места для хранения вновь вставленного B, ему придется перераспределить все свои элементы B в новое место хранения. Это, в свою очередь, изменит адреса всех этих B элементов.

Таким образом, вы можете создавать указатели, если можете гарантировать, что:

  • va перемещается вместо копирования.
  • Новые B s не добавляются в A после инициализации.

Предполагая, что вышеуказанное может быть гарантировано, вы можете создать vb следующим образом:

std::vector<B *> vb;

for (auto & a : va) {
  for (auto & b : a.v) {
    if (b.n == 1) { vb.push_back(&b); }
  }
}

или используя std::reference_wrapper:

std::vector<std::reference_wrapper<B>> vb;

for (auto & a : va) {
  std::copy_if(a.v.begin(), a.v.end(),
    std::back_inserter(vb),
    [](auto const & b) { return b.n == 1; });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...