Russian Qt Forum

Программирование => С/C++ => Тема начата: Igors от Сентябрь 16, 2011, 06:53



Название: Ну очень много приведений
Отправлено: Igors от Сентябрь 16, 2011, 06:53
Добрый день

Есть несколько простых template ф-ций, напр
Код
C++ (Qt)
template <class T1, class T2>
void Copy( const T1 * src, T2 * dst, size_t num )
{
for (size_t i = 0; i < num; ++i) dst[i] = src[i];
}
 
Беда в том что в момент вызова такой Copy я имею только 2 указателя на базовый тип Base, (а не фактический тип). Возможных типов 4, писать вместо каждой ф-ции 16 не хочется.  Также некоторые типы должны быть POD, так что dynamic_cast не проходит (хотя впрочем если бы и проходил - не писать же его 16 раз).  Как выкрутиться?

Спасибо


Название: Re: Ну очень много приведений
Отправлено: SimpleSunny от Сентябрь 16, 2011, 13:25
сделать оператор = виртуальным?


Название: Re: Ну очень много приведений
Отправлено: Igors от Сентябрь 16, 2011, 15:08
сделать оператор = виртуальным?
Наверное имелось ввиду что оператор = вызывает виртуальный метод. Но во всяком случае набор template ф-ций достаточно большой и оператор ничего не решит, напр
Код
C++ (Qt)
template <class T1, class T2>
void Scale( const T1 & src, T2 & dst )
{
dst.x = src.x / 2;
}
 


Название: Re: Ну очень много приведений
Отправлено: Akon от Сентябрь 16, 2011, 19:54
Хотите взаимоисключающих вещей. Если нужно статическое (на этапе комиляции) определение конкретной функции, каждый раз указывайте статические типы при вызове или сделайте 16 перегрузок. Если хотите одну функцию, то все ваши типы должны поддерживать соответствующий интерфейс, типа Clonable (ну или operator=, вызывающий clone):
Код:
class Clonable
{
public:
    virtual Clonable* clone() const = 0;
};

void Copy(const Clonable* src, Clonable** dst, size_t num)
{
 for (size_t i = 0; i < num; ++i) dst[i] = src[i]->clone();
}

Для поддержки не-Clonable типов (в т.ч. POD) можно сделать шаблон, как у вас; для Clonable - соответствующая перегрузка.


Название: Re: Ну очень много приведений
Отправлено: Igors от Сентябрь 17, 2011, 09:31
Если хотите одну функцию, то все ваши типы должны поддерживать соответствующий интерфейс, типа Clonable (ну или operator=, вызывающий clone):
Не понял Вашу идею. Метод clone может вернуть только указатель на Clonable, поэтому когда дело дойдет до оператора =, в левой и правой частях будут  ссылки на Clonable, т.е. присваивание  только базового класса.


Название: Re: Ну очень много приведений
Отправлено: alexman от Сентябрь 17, 2011, 10:39
Если хотите одну функцию, то все ваши типы должны поддерживать соответствующий интерфейс, типа Clonable (ну или operator=, вызывающий clone):
Не понял Вашу идею. Метод clone может вернуть только указатель на Clonable, поэтому когда дело дойдет до оператора =, в левой и правой частях будут  ссылки на Clonable, т.е. присваивание  только базового класса.
ну метод виртуальный. вы же можете его переопределить в наследниках.


Название: Re: Ну очень много приведений
Отправлено: Akon от Сентябрь 17, 2011, 10:45
Приведенная мной функция корректна. Там используются массивы указателей (по другому полиморфные или просто разные объекты единым образом (в одном контейнере) не сохранить).

Пример из реальной жизни:
Код:
class Exception : public std::exception
{
public:
...
  Exception* clone() const
  {
    Exception* result = createClone();
// check that a descendant overides createClone()
    assert(typeid(*result) == typeid(*this) && " (createClone() is not overrided)");
    return result; 
  }

private:
...
  virtual Exception* createClone() const
  {
    return new Exception(*this);  // copy ctor
  }
};


class InvalidArgumentException : public Exception
{
...
private:
  virtual InvalidArgumentException* createClone() const
  {
    return new InvalidArgumentException(*this);
  }


Название: Re: Ну очень много приведений
Отправлено: Igors от Сентябрь 17, 2011, 11:13
ну метод виртуальный. вы же можете его переопределить в наследниках.
Но я не могу переопределить тип возвращаемого значения, поэтому в template будет подаваться только Clonable (что в лучшем случае приведет к ошибке компилятора)

Код:
dst[i] = src[i]->clone();
clone вернет (Clonable *) и остается только разбираться в операторе = а кто же он такой (выяснять фактический тип). Это становится невыносимо уже для 3-4 порожденных классов. А если надо сделать напр так
Код
C++ (Qt)
dst[i].x = src[i].x;
 
То вообще неясно что делать - ведь базовый класс не имеет члена "x"


Название: Re: Ну очень много приведений
Отправлено: Akon от Сентябрь 17, 2011, 12:15
Но вы же класс dst не будете объявлясь как базовый, ведь так? Как базовый (Clonable) он будет использоваться в контексте Copy.

Код:
class Foo : public Clonable
{
  int x() const { ... }
...
}

std::vector<Foo*> dst(Count);
copy(src, &dst[0], Count);  // initialize array
dst[0]->x();

class Foo1 : public Clonable
{
  int y() const { ... }
...
}


std::vector<Foo1*> dst1(Count);
copy(src1, &dst1[0], Count);  // initialize array
dst[0]->y();

Если же вы хотите в одном контейнере сочетать Foo и Foo1, то никуда не уйти от dynamic_cast. Но такой аспект, как копирование, у вас решается раз и навсегда.


Название: Re: Ну очень много приведений
Отправлено: Igors от Сентябрь 18, 2011, 08:48
Но вы же класс dst не будете объявлясь как базовый, ведь так? Как базовый (Clonable) он будет использоваться в контексте Copy.
В этом и проблема
Код:
std::vector<Foo1*> dst1(Count);
copy(src1, &dst1[0], Count);  // initialize array
copy видит указатель на Clonable, до членов Foo1 дело не дойдет

Если же вы хотите в одном контейнере сочетать Foo и Foo1, то никуда не уйти от dynamic_cast. Но такой аспект, как копирование, у вас решается раз и навсегда.
Сочетать не хочу, мне нужно просто конвертить между 2 контейнерами с разными элементами. Напр все типы элементов совместимы по присваиванию и все имеют член х, но у одного это double, у другого float, int и.т.п. А dynamic_cast. не проблема, если нужен POD тип, то легко добавить ID типа в базовый класс (хоть бы sizeof).

Код:
class Foo : public Clonable
{
  int x() const { ... }
...
}
Так будет работать, но это фактически "все через virtual" (чтобы избежать template). Не очень удобно плюс заметим что x() должен вернуть "старший" тип (напр double) - коряво


Название: Re: Ну очень много приведений
Отправлено: Akon от Сентябрь 19, 2011, 16:44
Цитировать
copy видит указатель на Clonable, до членов Foo1 дело не дойдет
copy делает виртуальный вызов, и все члены классов наследников будут учтены в их методах createClone()

Цитировать
...мне нужно просто конвертить между 2 контейнерами с разными элементами. Напр все типы элементов совместимы по присваиванию и все имеют член х, но у одного это double, у другого float, int и.т.п.
Вы хотите присваивать несвязанные (в смысле нет общей базы) типы, но имеющие в интерфейсе сходные части?
Код:
struct Foo1
{
  double x;
  ...
}

struct Foo2
{
  int x;
  ...
}

template <typename T1, typename T2>
void copyItem(const T1& src, T& dst)
{
  dst.x = src.x;
  ...
}


Название: Re: Ну очень много приведений
Отправлено: Igors от Сентябрь 19, 2011, 16:57
Вы хотите присваивать несвязанные (в смысле нет общей базы) типы, но имеющие в интерфейсе сходные части?
Наоборот  :)
Беда в том что в момент вызова такой Copy я имею только 2 указателя на базовый тип Base, (а не фактический тип).


Название: Re: Ну очень много приведений
Отправлено: Akon от Сентябрь 19, 2011, 21:37
Неужели такое
Код:
struct Base {}

struct Foo1 : public Base
{
  double x;
  ...
}

struct Foo2 : public Base
{
  int x;
  ...
}

...
Foo1 foo1;
Foo2 foo2 = foo1;


Название: Re: Ну очень много приведений
Отправлено: Igors от Сентябрь 20, 2011, 05:06
Неужели такое
Код:
...
Foo1 foo1;
Foo2 foo2 = foo1;
С таким как раз проблем нет, т.к. фактические типы (Foo1, Foo2) на руках. Проблемы вот где
Код
C++ (Qt)
Base * test1 = new Foo1;
Base * test2 = new Foo2;
 
// Как теперь добраться до (например)
test1->x = test2->x;
 


Название: Re: Ну очень много приведений
Отправлено: Akon от Сентябрь 20, 2011, 20:51
ОК, теперь понятно. При таком объявлении про статический полиморфизм (шаблоны) можно забыть, если у Base нет поля x. Если есть Base::x, то нет проблем:
Код:
template <typename T1, typename T2>
void copyItem(const T1& src, T& dst)
{
  dst.x = src.x;
  ...
}


Название: Re: Ну очень много приведений
Отправлено: Igors от Сентябрь 21, 2011, 11:29
Если есть Base::x, то нет проблем:
Ну откуда же ему взяться - ведь наследники и задуманы чтобы хранить x и др данные в разных типах/форматах