Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум на Исходниках.RU > C/C++: Общие вопросы > шаблонный конструктор шаблонного класса да еще и с разным числом аргументов |
Автор: Dushevny 30.03.20, 20:48 |
имеется шаблонный класс: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> template<typename payload> class message { public: template<typename source> message(int param1, source param2); private: payload Payload; }; для разных специализаций этого шаблона я пишу разные конструкторы: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> template<> template<typename source> message<a>::message(int param1, source param2) { } template<> template<> message<b>::message(int param1, bool param2) { } но для одной из специализаций мне потребовался дополнительный параметр конструктора. Я, конечно, могу в исходный шаблон добавить второй конструктор с тремя аргументами, но считаю это не лучшим решением, потому что завтра мне для какой-то еще специализации может понадобится еще один аргумент и придется дописывать еще один конструктор. Попытался использовать variadic templates: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> .... public: template<typename ...Args> message(int param1, Args ...args); .... почти все хорошо: я могу описать полную специалзацию с любым количеством параметров: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> template<> template<> message<с>::message(int param1, int param2, bool param3) { } но мне нужно, чтобы один из параметров оставался шаблонным, то есть получить что-то вроде <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> Как это сделать? Если такое вообще возможно... template<> template<typename source> message<d>::message(int param1, source param2) { } template<> template<typename source> message<e>::message(int param1, int param2, source param3) { } |
Автор: Qraizer 30.03.20, 21:24 |
Цитата Dushevny @ Ты можешь его объявить, но реализовывать его будет только та специализация, которая сочтёт это нужным ей. Вызов такого конструктора для любой другой специализации вызовет ошибку линковки.Я, конечно, могу в исходный шаблон добавить второй конструктор с тремя аргументами, но считаю это не лучшим решением, потому что завтра мне для какой-то еще специализации может понадобится еще один аргумент и придется дописывать еще один конструктор. Я бы не стал считать это решение лучшим предыдущего. Скорее наоборот, оно неинуитивнее, ибо непонятнее зачем оно нужно. В специальном конструкторе смысл видится яснее. И скорее даже будет меньше вопросов "зачем он тут" по сравнению "что это вообще такое", если увидят вариадик. Но это субъективно, и зависит от конкретики ситуации. Добавлено P.S. Вообще же, это какая-то странная задача, когда надо для каждой пары шаблонных параметров мутить отдельные специализации. Обычно шаблоны используются с точностью до наоборот. Ты точно не мультиметоды пытаешься изобрести? |
Автор: Dushevny 30.03.20, 21:35 |
Qraizer, спасибо. Пожалуй, вы правы и оба решения хуже. Как говорится "стоило написать вопрос и тут же сам нашел ответ". Он, правда, не лучше первых двух, но в данной задаче меня устраивает - сделал наследника от нужной специализации, в конструкторе наследника обрабатываю "лишний" аргумент, в конструкторе специализации - два общих аргумента. тему закрывать не буду, вдруг кто-то все же напишет решение для второго варианта. Добавлено Цитата Qraizer @ Про мультиметоды даже не слышал, сейчас почитаю. Задача проста как грабли - имеется куча разных сообщений. Все они наследники одного базового класса, в котором определены виртуальные функции работы с этими сообщениями - распечатать, сравнить, отослать дальше. Каждый тип сообщения во входном потоке закодирован по-своему, конструктор разбирает входной поток и заполняет поля сообщения. А параметр этого потока шаблонный, потому что работа с этими сообщениями используется в разных программах и в одной из них входной поток - массив байтов, в других - массив классов (для каждого типа сообщений свой класс), для которых переопределен оператор приведения к байту. Вообще же, это какая-то странная задача, когда надо для каждой пары шаблонных параметров мутить отдельные специализации. Обычно шаблоны используются с точностью до наоборот. Ты точно не мультиметоды пытаешься изобрести? |
Автор: OpenGL 31.03.20, 05:58 |
Выглядит как будто тебе нужна перегрузка, а не специализация. |
Автор: Dushevny 31.03.20, 08:15 |
Фактически, да. Но объявление всех классов-наследников одинаковы (кроме конструктора в некоторых случаях), поэтому я обернул их в шаблон. Впрочем, ночью понял, что перемудрил с решением и буду все переделывать. |
Автор: Qraizer 31.03.20, 13:10 |
Пожалуй, шаблоны тут вообще не нужны. Достаточно обычного полиморфизма. |
Автор: Dushevny 31.03.20, 14:49 |
хорошо. Допустим, вот типы сообщений: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> struct a { int a; }; struct b { char b; }; struct c { long c; }; вот базовый класс: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> class generic_msg { public: virtual void show() const = 0; virtual void send() const = 0; }; вот решение с полиморфизмом: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> 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; }; Здесь их три, по факту - несколько десятков вот решение с шаблонами: <{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}> template<typename payload> class msg : public generic msg { public: void show() const override; void send() const override; private: payload Payload; }; почему бы их не использовать, если объявление шаблона одно на любое количество типов сообщений, а без шаблона на каждый тип сообщения приходится писать новое объявление потомка, которое отличается только типом поля payload? Добавлено Шаблоны ведь именно для этого и придуманы. Моя архитектурная ошибка была в том, что я заполнение полей сообщений возложил на конструктор, а надо в шаблоне сделать конструктор перемещения, принимающий объект типа payload. А уже этот объект создавать другим набором классов, который свой для каждой программы, использующей работу с этими сообщениями. |
Автор: Qraizer 31.03.20, 23:58 |
Не совсем для этого. Но это неважно. Архитектурная ошибка... даже скорее недочёт, заключается в том, что у тебя по факту два интерфейса, а реализовал ты как интерфейс лишь один, а другой жёстко в него вшит и не выделен как отдельная параметризируемая для него сущность. Для payload тоже надо было бы предусмотреть интерфейс с get()/set(), который бы абстрагировали его репрезентацию. <iostream> помнишь? std::basic_ios<> отдельно, а std::basic_streambuf<> отдельно. Оба находятся в тесной связи, но пульзуют друг друга полиморфно. Добавлено P.S. К слову сказать, тут мультиметод вполне мог бы всплыть как синергия между двумя независимыми деревьями. |