Russian Qt Forum
Ноябрь 23, 2024, 19:38 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Ну очень много приведений  (Прочитано 6088 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Сентябрь 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 раз).  Как выкрутиться?

Спасибо
Записан
SimpleSunny
Гость
« Ответ #1 : Сентябрь 16, 2011, 13:25 »

сделать оператор = виртуальным?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Сентябрь 16, 2011, 15:08 »

сделать оператор = виртуальным?
Наверное имелось ввиду что оператор = вызывает виртуальный метод. Но во всяком случае набор template ф-ций достаточно большой и оператор ничего не решит, напр
Код
C++ (Qt)
template <class T1, class T2>
void Scale( const T1 & src, T2 & dst )
{
dst.x = src.x / 2;
}
 
Записан
Akon
Гость
« Ответ #3 : Сентябрь 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 - соответствующая перегрузка.
« Последнее редактирование: Сентябрь 16, 2011, 19:56 от Akon » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Сентябрь 17, 2011, 09:31 »

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

Если хотите одну функцию, то все ваши типы должны поддерживать соответствующий интерфейс, типа Clonable (ну или operator=, вызывающий clone):
Не понял Вашу идею. Метод clone может вернуть только указатель на Clonable, поэтому когда дело дойдет до оператора =, в левой и правой частях будут  ссылки на Clonable, т.е. присваивание  только базового класса.
ну метод виртуальный. вы же можете его переопределить в наследниках.
Записан
Akon
Гость
« Ответ #6 : Сентябрь 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);
  }
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Сентябрь 17, 2011, 11:13 »

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

Код:
dst[i] = src[i]->clone();
clone вернет (Clonable *) и остается только разбираться в операторе = а кто же он такой (выяснять фактический тип). Это становится невыносимо уже для 3-4 порожденных классов. А если надо сделать напр так
Код
C++ (Qt)
dst[i].x = src[i].x;
 
То вообще неясно что делать - ведь базовый класс не имеет члена "x"
« Последнее редактирование: Сентябрь 17, 2011, 11:14 от Igors » Записан
Akon
Гость
« Ответ #8 : Сентябрь 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. Но такой аспект, как копирование, у вас решается раз и навсегда.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Сентябрь 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) - коряво
« Последнее редактирование: Сентябрь 18, 2011, 08:52 от Igors » Записан
Akon
Гость
« Ответ #10 : Сентябрь 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;
  ...
}
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Сентябрь 19, 2011, 16:57 »

Вы хотите присваивать несвязанные (в смысле нет общей базы) типы, но имеющие в интерфейсе сходные части?
Наоборот  Улыбающийся
Беда в том что в момент вызова такой Copy я имею только 2 указателя на базовый тип Base, (а не фактический тип).
Записан
Akon
Гость
« Ответ #12 : Сентябрь 19, 2011, 21:37 »

Неужели такое
Код:
struct Base {}

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

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

...
Foo1 foo1;
Foo2 foo2 = foo1;
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Сентябрь 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;
 
Записан
Akon
Гость
« Ответ #14 : Сентябрь 20, 2011, 20:51 »

ОК, теперь понятно. При таком объявлении про статический полиморфизм (шаблоны) можно забыть, если у Base нет поля x. Если есть Base::x, то нет проблем:
Код:
template <typename T1, typename T2>
void copyItem(const T1& src, T& dst)
{
  dst.x = src.x;
  ...
}
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.344 секунд. Запросов: 23.