Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.206.160.129] |
|
Сообщ.
#1
,
|
|
|
имеется шаблонный класс:
template<typename payload> class message { public: template<typename source> message(int param1, source param2); private: payload Payload; }; для разных специализаций этого шаблона я пишу разные конструкторы: template<> template<typename source> message<a>::message(int param1, source param2) { } template<> template<> message<b>::message(int param1, bool param2) { } но для одной из специализаций мне потребовался дополнительный параметр конструктора. Я, конечно, могу в исходный шаблон добавить второй конструктор с тремя аргументами, но считаю это не лучшим решением, потому что завтра мне для какой-то еще специализации может понадобится еще один аргумент и придется дописывать еще один конструктор. Попытался использовать variadic templates: .... public: template<typename ...Args> message(int param1, Args ...args); .... почти все хорошо: я могу описать полную специалзацию с любым количеством параметров: template<> template<> message<с>::message(int param1, int param2, bool param3) { } но мне нужно, чтобы один из параметров оставался шаблонным, то есть получить что-то вроде template<> template<typename source> message<d>::message(int param1, source param2) { } template<> template<typename source> message<e>::message(int param1, int param2, source param3) { } |
Сообщ.
#2
,
|
|
|
Цитата Dushevny @ Ты можешь его объявить, но реализовывать его будет только та специализация, которая сочтёт это нужным ей. Вызов такого конструктора для любой другой специализации вызовет ошибку линковки.Я, конечно, могу в исходный шаблон добавить второй конструктор с тремя аргументами, но считаю это не лучшим решением, потому что завтра мне для какой-то еще специализации может понадобится еще один аргумент и придется дописывать еще один конструктор. Цитата Dushevny @ Я бы не стал считать это решение лучшим предыдущего. Скорее наоборот, оно неинуитивнее, ибо непонятнее зачем оно нужно. В специальном конструкторе смысл видится яснее. И скорее даже будет меньше вопросов "зачем он тут" по сравнению "что это вообще такое", если увидят вариадик. Но это субъективно, и зависит от конкретики ситуации. Попытался использовать variadic templates: Добавлено P.S. Вообще же, это какая-то странная задача, когда надо для каждой пары шаблонных параметров мутить отдельные специализации. Обычно шаблоны используются с точностью до наоборот. Ты точно не мультиметоды пытаешься изобрести? |
Сообщ.
#3
,
|
|
|
Qraizer, спасибо. Пожалуй, вы правы и оба решения хуже.
Как говорится "стоило написать вопрос и тут же сам нашел ответ". Он, правда, не лучше первых двух, но в данной задаче меня устраивает - сделал наследника от нужной специализации, в конструкторе наследника обрабатываю "лишний" аргумент, в конструкторе специализации - два общих аргумента. тему закрывать не буду, вдруг кто-то все же напишет решение для второго варианта. Добавлено Цитата Qraizer @ Про мультиметоды даже не слышал, сейчас почитаю. Задача проста как грабли - имеется куча разных сообщений. Все они наследники одного базового класса, в котором определены виртуальные функции работы с этими сообщениями - распечатать, сравнить, отослать дальше. Каждый тип сообщения во входном потоке закодирован по-своему, конструктор разбирает входной поток и заполняет поля сообщения. А параметр этого потока шаблонный, потому что работа с этими сообщениями используется в разных программах и в одной из них входной поток - массив байтов, в других - массив классов (для каждого типа сообщений свой класс), для которых переопределен оператор приведения к байту. Вообще же, это какая-то странная задача, когда надо для каждой пары шаблонных параметров мутить отдельные специализации. Обычно шаблоны используются с точностью до наоборот. Ты точно не мультиметоды пытаешься изобрести? |
Сообщ.
#4
,
|
|
|
Выглядит как будто тебе нужна перегрузка, а не специализация.
|
Сообщ.
#5
,
|
|
|
Фактически, да. Но объявление всех классов-наследников одинаковы (кроме конструктора в некоторых случаях), поэтому я обернул их в шаблон.
Впрочем, ночью понял, что перемудрил с решением и буду все переделывать. |
Сообщ.
#6
,
|
|
|
Пожалуй, шаблоны тут вообще не нужны. Достаточно обычного полиморфизма.
|
Сообщ.
#7
,
|
|
|
хорошо. Допустим, вот типы сообщений:
struct a { int a; }; struct b { char b; }; struct c { long c; }; вот базовый класс: class generic_msg { public: virtual void show() const = 0; virtual void send() const = 0; }; вот решение с полиморфизмом: class msg_a : public generic_msg { public: void show() const override; void send() const override; private: a Payload; }; class msg_b : public generic_msg { public: void show() const override; void send() const override; private: b Payload; }; class msg_c : public generic_msg { public: void show() const override; void send() const override; private: c Payload; }; Здесь их три, по факту - несколько десятков вот решение с шаблонами: template<typename payload> class msg : public generic msg { public: void show() const override; void send() const override; private: payload Payload; }; почему бы их не использовать, если объявление шаблона одно на любое количество типов сообщений, а без шаблона на каждый тип сообщения приходится писать новое объявление потомка, которое отличается только типом поля payload? Добавлено Шаблоны ведь именно для этого и придуманы. Моя архитектурная ошибка была в том, что я заполнение полей сообщений возложил на конструктор, а надо в шаблоне сделать конструктор перемещения, принимающий объект типа payload. А уже этот объект создавать другим набором классов, который свой для каждой программы, использующей работу с этими сообщениями. |
Сообщ.
#8
,
|
|
|
Цитата Dushevny @ Не совсем для этого. Но это неважно. Архитектурная ошибка... даже скорее недочёт, заключается в том, что у тебя по факту два интерфейса, а реализовал ты как интерфейс лишь один, а другой жёстко в него вшит и не выделен как отдельная параметризируемая для него сущность. Для payload тоже надо было бы предусмотреть интерфейс с get()/set(), который бы абстрагировали его репрезентацию. <iostream> помнишь? std::basic_ios<> отдельно, а std::basic_streambuf<> отдельно. Оба находятся в тесной связи, но пульзуют друг друга полиморфно. Шаблоны ведь именно для этого и придуманы. Добавлено P.S. К слову сказать, тут мультиметод вполне мог бы всплыть как синергия между двумя независимыми деревьями. |