Контейнерное литье
У меня есть два класса и мои методы преобразования:
class A;
class B;
class A
{
...
};
class B
{
...
static B fromA(A a)
{
B b;
// ... some property conversions from A to B
return b;
}
operator A()
{
// ... some property conversions from this(B) to A
return A
}
}
Как видно, я определил приведение (для B -> A) + статическое преобразование (для A -> B) внутри класса B. Запрещено определять любое приведение или преобразование внутри класса A.
Предположим, у меня есть два вектора:
vector<vector<A> > vecA; // pre-defined
vector<vector<B> > vecB; // to be casted from vecA
Какой оптимальный (самый быстрый) способ добиться преобразования данных между ними, вместо моего уродливого решения:
using namespace std;
vecB.resize(vecA.size());
for(int i = 0; i<vecA.size(); i++)
{
vecB[i].resize(vecA[i].size());
for(int j = 0; j<vecA[i].size(); j++)
{
vecB[i][j] = B::fromA(vecA[i][j]);
}
}
6 ответов
Определить оператор = для B
B::operator=(const A& a)
{
// actual code to copy from a to b.
}
в вашем коде поменяйте строку
vecB[i][j] = B::fromA(vecA[i][j]);
в
vecB[i][j] = vecA[i][j];
это исключит многие временные объекты A & B, созданные в вашем исходном коде.
Также вместо использования vecB[i][j] используйте итераторы. Следующий код может иметь некоторые синтаксические ошибки
vecB.resize(vecA.size());
vector<vector<A> >::const_iterator itA1;
vector<vector<B> >::const_iterator itB1;
vector<A> >::const_iterator itA2;
vector<B> >::const_iterator itB2;
for(itA1=vecA.begin(), itB1= vecB.begin();
itA1 != vecA.end();
++itA1, ++itB1)
{
for(itA2 = (*itA1).begin(), itB2=(*itB1).begin();
itA2 != (*itA1).end();
++itA2, ++itB2)
{
(*itB2) = *itA2;
}
}
Вместо определения:
static B fromA(A a);
определить:
B(A a)
как конструктор преобразования
как правило, этого можно избежать с помощью explicit
ключевое слово, но здесь это, кажется, именно то, что вы хотите, так как вам нужно построить B из A
это упростило бы:
vecB[i][j] = B::fromA(vecA[i][j]);
чтобы:
vecB[i][j] = vecA[i][j];
который выглядит как хороший кандидат для `std::copy
Вы можете определить конструктор неявного преобразования для преобразования A
в B
вместо именованной функции:
B(A const & a) {
// ... some property conversions from A to B
}
Теперь вы можете конвертировать один вектор с чем-то вроде
std::vector<B> vecB(vecA.begin(), vecA.end()); // declare a new vector
vecB.assign(vecA.begin(), vecA.end()); // reassign an existing vector
Вам все еще понадобится цикл для переназначения вектора векторов; возможно что-то вроде:
vecB.clear();
vecB.reserve(vecA.size());
for (auto const & vA : vecA) {
vecB.push_back(std::vector<B>(vA.begin(), vA.end()));
}
Это не быстрее, чем ваша версия, но включает в себя написание меньшего количества кода.
Вы можете использовать std::transform, чтобы сделать ваш код немного короче и проще для чтения. Однако я думаю, что вы получите лучшую производительность, если вы определите метод B fromA(A a)
пустой метод, который принимает выходной параметр типа B&
, Т.е. сделать декларацию вашего метода static void fromA(const A&, B&)
, Таким образом, для кода до с ++11 вы избежите копирования возвращаемого объекта.
В качестве альтернативы сделать метод fromA
метод экземпляра вместо статического метода и снова делает его недействительным - метод изменяет текущий экземпляр. Другой вариант, как указано в других ответах, - создать конструктор B, который принимает const A&
как его единственный параметр.
Вы можете использовать алгоритмы и итераторы вместо созданных вручную for
циклы, если вы хотите (это может быть немного быстрее, если ваша стандартная реализация библиотеки сильно оптимизирована), но основная сложность остается той же - вы должны конвертировать каждый элемент по отдельности, никак иначе.
Если вы хотите написать хороший код, вы можете использовать либо std::transform
или же std::assign
, или же std::for_each
но если вам нужен быстрый код, тогда будет сложно написать что-то быстрее, чем это:
vecB.resize(vecA.size());
for (unsigned int i = 0, asize = vecA.size(); i < asize; i++)
{
const vector<A> & vin = vecA[i];
vector<A> & vout = vecB[i];
vout.resize(vin.size());
for (unsigned int j = 0, bsize = vout.size(); j < bsize; j++)
{
vout[j].createFromA(vin[j]);
}
}
Обратите внимание на asize
а также bsize
инициализация выполняется так, чтобы избежать проблем с наложением. Также createFromA
должна быть функцией-членом, которая непосредственно инициализирует членов класса без копирования / назначения другого класса.