c ++ 11 — C ++: std :: vector — возможно «разрезать» вектор?

Я пишу код для интеграции ODE. Этот вопрос является как просьбой о совете по кодированию, так и решением, поэтому, если у вас есть альтернативное предложение, которое я собираюсь предложить, пожалуйста, дайте мне знать!

«Объекты», которые должны быть интегрированы интегратором ODE, входят в «блоки» из 6 … Причина этого заключается в том, что у меня есть std :: vector значений double, и они расположены следующим образом:

Первые 3 двойных являются координатами положения; х, у и z.
Следующие 3 двойных являются скоростными координатами; х, у и z.

Итак, теперь вы знаете, что у меня есть функция, которая принимает пары «позиции» «» «векторы» «» в качестве аргументов и возвращает какой-то результат … Видите, куда я иду с этим?

В настоящее время функция ожидает 2 лота координат позиции следующим образом:

std::vector<double> magic_gravity_formula(const std::vector<double> &r1,
const std::vector<double> &r2, const double m1, const double m2)

Я не хочу копировать все данные в группе 3 в новые векторы — это безумный (и очень медленный) способ что-то программировать.

я мог вместо этого используйте указатели на необработанные данные … и просто передайте указатель на координату x (первый элемент в блоке из трех двойных чисел) — мне кажется, это нормально, но, возможно, есть лучший способ? Что-то вроде нарезки массивов в Python или Matlab? Могу ли я сделать что-то подобное?

Я хочу передать новый вектор (или какой-то класс-обертку?), Созданный из данных, уже хранящихся в массиве … Что-то вроде

std::vector<double> sub_section_of_data = data[0..2] // Obviously pseudocode!

Итак, вышесказанное бессмысленно, потому что, по-видимому, язык, в котором реализован этот синтаксис, все равно будет выполнять операцию копирования, которая, вероятно, будет медленной — именно то, чего я пытаюсь избежать …

Так что да, я не уверен, что это лучший способ для этого — кто-нибудь может предложить «хорошее» решение? (Не субъективно!)

РЕДАКТИРОВАТЬ: Чтобы сделать это действительно ясно — проблема в том, что я не хочу делать что-то вроде:

std::vector<double> r1_temp;
r1_temp.push_back(data[0]); // Copy ! Bad !
r1_temp.push_back(data[1]);
r1_temp.push_back(data[2]);

... same for an r2 ...

std::vector<double> force = magic_gravity_formula(r1, r2, m1, m2);

РЕДАКТИРОВАТЬ 2: Рассмотрите параметры компилятора — будет ли компилятор оптимизировать мой код для меня, выполнив что-то вроде изменения функции для приема аргументов следующим образом:

std::vector<double> super_gravity_formula(double x1, double y1, double z1, double x2, double y2, double z2, double m1, double m2)

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

Редактировать 3: Поэтому это все еще важно.

5

Решение

Вы хотите посмотреть на существующий вектор.

std::experimental::array_view<T>Или сверните свое собственное.

Массив представляет собой пару T*и аксессоры, которые позволяют вам рассматривать это как массив operator[] .begin() .back() size() empty() и т.п.

Вот одна из многих таких реализаций. Это немного тяжелый вес, с range<Iterator> представление, что адаптивно обрабатывает итераторы произвольного доступа, и array_view<T> что наследует от range<T*>,

Если это не сработает, найдите другой пост в SO по «yakk» со словом «array_view» или отладьте его самостоятельно. Я написал это по крайней мере полдюжины раз, с более или менее отладкой, с разными const правила правильности.

Как только вы это сделаете, { vec.data()+index, vec.data()+index+3 } построит array_view<double> с нулевыми накладными расходами.

6

Другие решения

Если то, что вы хотите, это векторы, в математическом смысле, из 6 элементов типа double, std::vector<double> может быть не самое эффективное представление. Я бы просто определил трехмерную точку, а затем положение + скорость как:

struct point {
double x, y, z;
};
struct position_and_velocity {
point pos;
point vel;
};

Работа с этими типами, вероятно, будет более эффективной, чем работа с векторами (без динамического распределения), и вы можете просто работать с этим как ценности. Полная копия этой структуры, вероятно, дешевле, чем вставка первого элемента в std::vectorи немного медленнее, но не намного, чем предоставление двух указателей в массив, чтобы получить ломтик.

Кроме того, он будет менее подвержен ошибкам, как в случае нарезка массив, который вы можете по ошибке использовать неправильные значения и в итоге получить такие вещи, как (псевдокод) slice[1..3] это две координаты положения с одной из координат скорости (возможно, бессмысленной).

Сигнатура функции является частью двоичного интерфейса вашей программы, компилятор не изменит сигнатуру функции, пока он не вставит код в код. Кроме того, я бы очень сомневался, что компилятор удалит динамическое распределение, присущее векторам в любом из преобразований (если он не может удалить всю переменную).

1

Поскольку код требует const std::vector<double> & ты не можешь быть умнее.

В противном случае (если бы библиотечные функции были более общими), вы могли бы легко передать диапазон (см., Например, Boost Range, но вы могли бы легко выделить единичный векторный поддиапазон так же, как boost::string_ref или же std::string_view)

0

«Я мог бы вместо этого использовать указатели на необработанные данные … и просто передать указатель на координату х».

Вы сами ответили на свой вопрос. std :: алгоритмы ничего не делают, иначе.

0