Лучшие функции преобразования - PullRequest
3 голосов
/ 02 апреля 2012

У меня есть различные классы в среде C ++ 0x, и я хотел бы написать функции для преобразования между некоторыми из них.Например:

struct Foo { float v; };
struct Bar { int i; };
struct Tar { float p, q; };

void Convert(const Foo& x, Bar& y) { y.i = static_cast<int>(x.v); }
void Convert(const Tar& x, Foo& y) { y.v = x.p + x.q; }

Это только пример.Здесь довольно много «маленьких» классов.И не все функции преобразования имеют смысл.

Кроме того, есть некоторые классы, которые по сути ведут себя как контейнеры STL и должны «наследовать» эти функции преобразования.

void Convert(const std::vector<Foo>& cx, std::vector<Bar>& cy) { ... }
void Convert(const std::vector<Tar>& cx, std::vector<Bar>& cy) { ... }

Нет, я ищу простойспособ определения этих функций.Я попытался:

template<typename X, typename Y>
void Convert(const std::vector<X>& cx, std::vector<Y>& cy) {
   cy.resize(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     Convert(cx[i], cy[i]);
   }
}

, и это прекрасно работает.

Однако при такой настройке нужно написать

std::vector<X> cx = { ... };
std::vector<Y> cy;
Convert(cx, cy);
// when not specifying the type, one needs to use this form f(X, &Y)

При такой настройке, как

template<typename X, typename Y>
std::vector<Y> Convert(const std::vector<X>& cx) {
   std::vector<Y> cy(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     cy[i] = Convert(cx[i]);
   }
   return cy;
}

нужно написать

std::vector<X> cx = { ... };
std::vector<Y> cy = Convert<X,Y>(cx);
// can I avoid specifying the source type with this form?

Конечно, в какой-то момент необходимо упомянуть тип цели, но тип источника определяется параметром функции.Я не хочу упоминать это снова и снова.

Существует ли элегантный общий способ обработки таких функций преобразования?

Отредактировал вопрос для уточнения

Ответы [ 4 ]

1 голос
/ 02 апреля 2012

Почему бы просто не использовать конструкторы для этой цели?

explicit Bar(const Foo& x) : i(static_cast<int>(x.v)) {}
1 голос
/ 02 апреля 2012

Рассматривали ли вы использование ctors:

struct Bar {
   int i;

   explicit Bar(Foo const &f) : i(static_cast<int>(f.v)) {}
};

Тогда вы можете сделать:

Bar x = static_cast<Bar>(some_foo);

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

std::vector<foo> foos;

// code to populate foos ...

std::vector<Bar> bars((foos.begin()), foos.end());

Что касается того, делать это явным или нет, вопрос в том, сможете ли вы получить преобразование из Foo в Barслучайно, позволяя случиться тому, чего вы действительно не хотите.Это довольно распространенная проблема с такими вещами, как преобразование в bool, но гораздо реже проблема с преобразованием из одного пользовательского типа в другой.В этом случае отключение explicit может быть удобным и достаточно безопасным.

0 голосов
/ 30 мая 2012

На самом деле я узнал, как это сделать. Переупорядочив аргументы шаблона

template<typename Y, typename X>
std::vector<Y> ConvertTo(const std::vector<X>& cx) {
   std::vector<Y> cy(cx.size());
   for(std::size_t i=0; i<cx.size(); i++) {
     cy[i] = Convert(cx[i]);
   }
   return cy;
}

можно написать

std::vector<Y> cy = ConvertTo<Y>(cx);

и компилятор автоматически выводит второй тип шаблона для ввода. Приятная особенность!

0 голосов
/ 02 апреля 2012

Из-за Оптимизация возвращаемого значения вам не нужно использовать параметр out, просто используйте возвращаемое значение:

void Convert(const X& x, Y& y);

становится:

Y Convert(const X& x);

так что теперь вы можете использовать его в выражении.

Также рассмотрите возможность использования конструкторов преобразования или операторов преобразования вместо:

Y::Y(const X&);
X::operator Y();

Это позволит неявное преобразование между типами (если это то, что вы хотите - в противном случае вы можете объявить их явным образом).

Для обработки контейнеров STL используйте диапазоны итераторов:

template<class FwdIterator>
Y Convert(FwdIterator begin, FwdIterator end);

В этом стиле он не только будет совместим со всеми контейнерами STL, но и с необработанными диапазонами указателей.

Также обратите внимание, если вы объявляете неявные конструкторы преобразования или операторы преобразования, чем вы можете использовать std :: copy:

vector<X> xs = ...;
vector<Y> ys;
copy(xs.begin(), xs.end(), back_inserter(ys.begin()));

Это будет вызывать ваши пользовательские преобразования неявно при преобразовании элементов из xs в ys.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...