Каков наиболее эффективный способ проецирования структурированных данных в std :: vector?

Допустим, у меня есть следующие типы структур:

struct XStruct {
int a_value;
int b_value;
}

struct YStruct {
int c_value;
int d_value;
};

Теперь у меня есть следующий вектор:

std::vector<XStruct> m_x_values;

И я хочу проецировать свои данные так, чтобы c_values = b_valueс и d_values = a_values

std::vector<YStruct> m_y_values;

Какой самый эффективный способ сделать это:

Опция 1:

m_y_values.clear();
m_y_values.reserve(m_x_values.size());

for(auto x : m_x_values)
{
m_y_values.push_back({x.b_value, x.a_value});
}

Вариант 2:

m_y_values.resize(m_x_values.size());
int i = 0;
for(auto x : m_x_values)
{
m_y_values[i].c_value = x.b_value
m_y_values[i].d_value = x.a_value;
++i;
}

Любое другое предложение?

2

Решение

Вы можете создать конструктор второй структуры следующим образом:

struct YStruct {
explicit YStruct(const &XStruct x_struct)
: c_value(x_struct.a_value)
, d_value(x_struct.b_value)
{};

int c_value;
int d_value;
};

Вы сможете использовать y_struct = x_struct или же YStruct y_struct(x_struct),
Затем просто скопируйте каждый элемент первого вектора во второй.

2

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

алгоритм STL transform подходит для такого рода проблем.

std::vector<XStruct> x_values = { { 1, 2 }, {3, 4} };
std::vector<YStruct> y_values(x_values.size());

std::transform(x_values.begin(), x_values.end(), y_values.begin(), [](const XStruct& x){
return YStruct{ x.b_value, x.a_value };
});

Или использовать vector::emplace_back сохранить немного YStruct время построения, но это займет некоторое время, когда вектор будет изменен.

std::vector<XStruct> x_values = { { 1, 2 }, {3, 4} };
std::vector<YStruct> y_values;

for (const auto& x : x_values){
y_values.emplace_back(YStruct{ x.b_value, x.a_value });
}
1

std::vector может быть инициализирован с помощью пары итераторов, и это позволяет использовать эту идиому:

std::vector<A> new_vect(old_vect.begin(), old_vect.end());

таким образом, определяя конструктор для Y принимая const X& в качестве параметра вы будете выражать свое намерение настолько четко и кратко, насколько это возможно, предоставляя библиотеке свободу делать все, что лучше для выполнения операции.

Делегирование в библиотеку и надежда на то, что она делает все возможное, — это не стратегия, которая всегда побеждает (на самом деле вовсе), а в случае std::vector Я был бы достаточно уверен, что разработчики компилятора сделали все возможное, чтобы выполнить операцию как можно быстрее.

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

Для моего опыта я бы ожидал reserve + push_back Версия худший подход с точки зрения производительности, потому что компиляторы сегодня не достаточно умны (AFAIK), чтобы обнаружить этот паттерн. Даже назначение каждого элемента может быть не самым быстрым способом, потому что когда вы пишете

v[i] = x;

а также v является std::vector двойная косвенность необходима, потому что вектор содержит указатель на то, где находятся данные. В некоторых случаях я был вынужден явно использовать подход:

X* vp = &v[0];
for (int i=0,n=v.size(); i<n; i++) {
vp[i] = ...
}

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

0
По вопросам рекламы [email protected]