Шаблоны C++ и наследование

В моей структуре C++ есть кнопки. Кнопка является производным от Control. Таким образом, функция, принимающая Control, может принимать в качестве аргумента кнопку. Все идет нормально.

Еще у меня есть List<T>. Однако List<Button> не является производным от List<Control>, что означает, что функция, принимающая список элементов управления, не может принимать список кнопок в качестве аргумента. Это прискорбно.

Возможно, это глупый вопрос, но я не понимаю, как решить эту проблему :( List<Button> должен быть производным от List<Control>, но я не вижу способа сделать это «автоматически».

Ладно, плохо. У меня есть списки указателей, а не списки объектов. И, конечно, сохранение моих Buttons в списке Control решит проблему с вызовом функции, и я обычно это делаю, за исключением случаев, когда я хочу, чтобы список Button * использовал их как Button, а не как Control.

ggambett 24.11.2008 23:08

Что у вас есть? Только List <Button *> и List <Control *> или List <Control *>? Я запутался. пожалуйста, сформулируйте вопрос :)

Johannes Schaub - litb 24.11.2008 23:22

если у вас есть только List <Control *> и вы хотите использовать отдельные элементы в особом случае, если они имеют тип кнопки, вы будете вынуждены использовать dynamic_cast mate.

Johannes Schaub - litb 24.11.2008 23:23

Какой у вас список? Надеюсь, вы не изобретаете заново идеально хорошие структуры данных, такие как std :: vector?

jalf 24.11.2008 23:36

У меня есть этот недействительный doSomething (List <Control *> lControls); void foo (void) {Список <Кнопка *> lButtons; .... doSomething (lButtons); // Я хочу это сделать, но не могу}

ggambett 24.11.2008 23:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
5
4 848
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Как насчет использования указателей? Просто имейте список list<Control*> и поместите в него любые объекты, производные от Control.

Если вы все же решите использовать список указателей, вам нужно будет не забыть удалить объекты, когда вы закончите с ними (вы потеряете автоматическое управление памятью). Чтобы вернуть его, см. Библиотеку Boost ptr_container. У него есть контейнер, который управляет списком указателей, как если бы это был стандартный список.

Tom Leys 24.11.2008 23:08

Вместо использования List <Button> используйте List <Control *>, которые указывают на кнопки. Таким образом, ваша функция должна принимать только один тип: List <Control *>.

Извините за непонятность. Я использую указатели (точнее, умные указатели). Функция принимает список управления *. Дело в том, что я хочу передать ему список Button *.

ggambett 24.11.2008 23:10

Страуструп написал по этому поводу в своем 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 когда-нибудь. Не очень.

Michel 24.11.2008 23:18

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

BCS 26.11.2008 09:21

Как правило, способ C++ для написания алгоритмов, которые работают со списком, последовательностью, ... заключается в предоставлении итераторов в качестве аргументов.

template < class iterator >
doSomething(iterator beg, iterator end);

Это решает, что List <Button *> не является производным от List <Control *>. (использование шаблона List <T *> тоже будет, но это как бы делает вашу функцию универсальной, но не совсем)

По моему опыту, создание хороших шаблонных функций, работающих на итераторах, может потребовать много (слишком много ...) работы, но это "путь C++" ...

Если вы идете по этому маршруту, подумайте об использовании Boost.ConceptCheck. Это сделает вашу жизнь намного проще.

Другие вопросы по теме