В моей структуре C++ есть кнопки. Кнопка является производным от Control. Таким образом, функция, принимающая Control, может принимать в качестве аргумента кнопку. Все идет нормально.
Еще у меня есть List<T>. Однако List<Button> не является производным от List<Control>, что означает, что функция, принимающая список элементов управления, не может принимать список кнопок в качестве аргумента. Это прискорбно.
Возможно, это глупый вопрос, но я не понимаю, как решить эту проблему :( List<Button> должен быть производным от List<Control>, но я не вижу способа сделать это «автоматически».
Что у вас есть? Только List <Button *> и List <Control *> или List <Control *>? Я запутался. пожалуйста, сформулируйте вопрос :)
если у вас есть только List <Control *> и вы хотите использовать отдельные элементы в особом случае, если они имеют тип кнопки, вы будете вынуждены использовать dynamic_cast mate.
Какой у вас список? Надеюсь, вы не изобретаете заново идеально хорошие структуры данных, такие как std :: vector?
У меня есть этот недействительный doSomething (List <Control *> lControls); void foo (void) {Список <Кнопка *> lButtons; .... doSomething (lButtons); // Я хочу это сделать, но не могу}





Как насчет использования указателей? Просто имейте список list<Control*> и поместите в него любые объекты, производные от Control.
Если вы все же решите использовать список указателей, вам нужно будет не забыть удалить объекты, когда вы закончите с ними (вы потеряете автоматическое управление памятью). Чтобы вернуть его, см. Библиотеку Boost ptr_container. У него есть контейнер, который управляет списком указателей, как если бы это был стандартный список.
Вместо использования List <Button> используйте List <Control *>, которые указывают на кнопки. Таким образом, ваша функция должна принимать только один тип: List <Control *>.
Извините за непонятность. Я использую указатели (точнее, умные указатели). Функция принимает список управления *. Дело в том, что я хочу передать ему список Button *.
Страуструп написал по этому поводу в своем FAQ:
Почему я не могу назначить vector<Apple*> на vector<Fruit*>
Решить ее можно двумя способами:
Control. Тогда примите List<Control*>List<Button> и List<Control>, но это скорее шаблонный код, и в большинстве случаев это не обязательно.Вот код для второй альтернативы. Первая альтернатива уже объясняется другими ответами:
class MyWindow {
template<typename T>
void doSomething(List<T> & l) {
// do something with the list...
if (boost::is_same<Control, T>::value) {
// special casing Control
} else if (boost::is_same<Button, T>::value) {
// special casing Button
}
}
};
Чтобы ограничить doSomething только для List<derived from Control>, необходим дополнительный код (ищите enable_if, если хотите знать).
Обратите внимание, что такого кода (смотря какой у вас тип) лучше избегать. Вы должны справиться с такими вещами с помощью виртуальных функций. Добавьте функцию doSomething в Control и переопределите ее в Button.
Ненавижу говорить вам, но если вы используете список экземпляров для Control вместо указателей на Control, ваши кнопки в любом случае будут мусором (Google "нарезка объектов"). Если это списки указателей, то либо превратите list<button*> в list<control*>, как предлагали другие, либо сделайте копию на новый list<control*> из list<button*> и вместо этого передайте который в функцию. Или перепишите функцию как шаблон.
Итак, если у вас раньше была функция с именем doSomething, которая принимала список элементов управления в качестве аргумента, вы бы переписали ее как:
template <class TControl>
void doSomething( const std::list<TControl*>& myControls ) {
... whatever the function is currently doing ...
}
void doSomethingElse() {
std::list<Button*> buttons;
std::list<Control*> controls;
doSomething( buttons );
doSomething( controls );
}
Это выглядит некрасиво, но это сердце стандартной библиотеки шаблонов и современного программирования на C++. Проверьте исходный код самого std :: list когда-нибудь. Не очень.
Моя основная проблема с этим заключается в том, что (IIUC) компилятор совершенно законно генерирует бит для битовых идентичных функций для каждого используемого типа. Ну что ж.
Как правило, способ C++ для написания алгоритмов, которые работают со списком, последовательностью, ... заключается в предоставлении итераторов в качестве аргументов.
template < class iterator >
doSomething(iterator beg, iterator end);
Это решает, что List <Button *> не является производным от List <Control *>. (использование шаблона List <T *> тоже будет, но это как бы делает вашу функцию универсальной, но не совсем)
По моему опыту, создание хороших шаблонных функций, работающих на итераторах, может потребовать много (слишком много ...) работы, но это "путь C++" ...
Если вы идете по этому маршруту, подумайте об использовании Boost.ConceptCheck. Это сделает вашу жизнь намного проще.
Ладно, плохо. У меня есть списки указателей, а не списки объектов. И, конечно, сохранение моих Buttons в списке Control решит проблему с вызовом функции, и я обычно это делаю, за исключением случаев, когда я хочу, чтобы список Button * использовал их как Button, а не как Control.