Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.57/21: Рейтинг темы: голосов - 21, средняя оценка - 4.57
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 335
1

Определение типа - наследника

16.08.2017, 21:30. Показов 3888. Ответов 12
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Здравствуйте!

Проблема в следующем: контейнер хранить любого наследника некоторого базового класса.
Есть шаблонный метод, который в зависимости от наследника выполняет свои действия.
Шаблонный метод видит всегда только базовый и не различает наследников

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Base1 {};
class Base2 : public Base1 {};
class Base3 : public Base2 {};
 
template<class T>
void foo(T)
{
    std::cout << "T" << std::endl;
}
 
template<>
void foo(Base2)
{
    std::cout << "Base2" << std::endl;
}
 
template<>
void foo(Base3)
{
    std::cout << "Base3" << std::endl;
}
 
std::vector<Base1> vec;
 
int main()
{
    vec.push_back(Base1());
    vec.push_back(Base2());
    vec.push_back(Base3());
    foo(vec[0]);
    foo(vec[1]);
    foo(vec[2]);
 
//Вывод всегда:
// T
}
Каким образом можно построить архитектуру, чтобы контейнер мог хранить наследников и в дальнейшем передавать их как параметры в шаблонный метод?
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
16.08.2017, 21:30
Ответы с готовыми решениями:

Указатель типа базового класса на тип наследника
Встретил примерно такое: // Example program #include &lt;iostream&gt; #include &lt;string&gt; using...

Определение типа!
У меня такой вопрос. Как можно узнать какой тип вводится? Например так: int a; int b; cout &lt;&lt;...

Определение типа объекта
Здравствуйте! Помогите пожалуйста. Есть абстрактный класс и 3 потомка. class Transport {...

Определение типа переменной
#include &lt;iostream&gt; #include &lt;typeinfo&gt; int main() { int t = 10; std::cout &lt;&lt;...

12
4 / 4 / 0
Регистрация: 27.05.2017
Сообщений: 26
16.08.2017, 22:00 2
Не совсем понял что ты хочешь сделать, но для того что бы хранить разные типы тебе может помочь кортеж, std::tuple, а касательно того что ты сейчас сделал.
vec[0] возвращает тип изначально заданный, то есть std::vector<Base1> vec;
для определения передаваемого типа можно воспользоваться typeid(T).name().

Добавлено через 1 минуту
C++
1
2
3
4
5
template<class T>
void foo(T)
{
    std::cout << typeid(T).name() << std::endl;
}
Добавлено через 4 минуты
Ну вот посмотри.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <tuple>
#include <vector>
 
class Base1 {};
class Base2 : public Base1 {};
class Base3 : public Base2 {};
 
template<class T>
void foo(T)
{
    std::cout << typeid(T).name() << std::endl;
}
 
int main()
{
 
    auto vec = std::make_tuple(Base1(), Base2(), Base3());
 
    foo(
        std::get<0>(vec)
    );
    foo(
        std::get<1>(vec)
    );
    foo(
        std::get<2>(vec)
    );
 
    system("pause");
    return 0;
}
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 335
16.08.2017, 22:08  [ТС] 3
TerroJames,
Суть именно в специализации шаблона. Каждая функция заточено под свой тип (Но все типы наследуются от одного).
Я храню в контейнере потомков и затем мне нужно вызвать функцию от этого потомка. То есть компилятор должен сам определить какой тип на самом деле лежит в Base1 (Это может быть любой его потомок).
Tuple с этой задачей справится только если последовательность типов не меняется. Но это не совсем то...
Цитата Сообщение от TerroJames Посмотреть сообщение
Не совсем понял что ты хочешь сделать
Я хочу, чтобы вызывалась не базовая функция, а ее специализации в зависимости от типа. Но вектор определен как контейнер для Base1 и, видимо, нельзя в определить типы его потомков.
0
4 / 4 / 0
Регистрация: 27.05.2017
Сообщений: 26
16.08.2017, 22:14 4
Не, то что я тебе сейчас кинул это просто пример того что тебе надо будет сделать, а typeid(T).name(). для понимания что ты именно приходит.
Тоже самое работает и так.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Base1 {};
class Base2 : public Base1 {};
class Base3 : public Base2 {};
 
void foo(Base1 foo)
{
    std::cout << "b1" << std::endl;
}
 
void foo(Base2 foo)
{
    std::cout << "b2" << std::endl;
}
 
void foo(Base3 foo)
{
    std::cout << "b3" << std::endl;
}
 
int main()
{
 
    auto vec = std::make_tuple(Base1(), Base2(), Base3());
 
    foo(
        std::get<0>(vec)
    );
    foo(
        std::get<1>(vec)
    );
    foo(
        std::get<2>(vec)
    );
 
    system("pause");
    return 0;
}
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 335
16.08.2017, 22:21  [ТС] 5
Цитата Сообщение от TerroJames Посмотреть сообщение
Не, то что я тебе сейчас кинул это просто пример того что тебе надо будет сделать, а typeid(T).name(). для понимания что ты именно приходит.
Тоже самое работает и так.
Дело в том, что контейнер наполняется в runtime/
А если нужно добавить в контейнер заранее неизвестный тип - наследник Base1? Вот тут и загвоздка
0
4 / 4 / 0
Регистрация: 27.05.2017
Сообщений: 26
16.08.2017, 22:43 6
То есть типо там может быть много данных ??

Добавлено через 3 минуты
Или стоп, ты имеешь в виду может быть много наследников ? неизвестное количество??

Добавлено через 14 минут
Ну, перечитав то что ты написал, я примерно понял что ты хочешь, Вообщем, тебе нужно скорее всего бинарное дерево, не помню если оно в stl, Ну вот там от одной переменной можно будет вызывать разно специализированные функции от всех наследников для одного типа, но и там мне кажется придётся использовать std::tuple.
0
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 335
16.08.2017, 22:44  [ТС] 7
Цитата Сообщение от TerroJames Посмотреть сообщение
Или стоп, ты имеешь в виду может быть много наследников ? неизвестное количество??
Да. Вектор содержит множество наследников. Все это заполняется в рантайме.
Далее хочу передать этих наследников в шаблонный метод. Нужно чтобы из vector<Base*> вызывался foo(vec[0]) с нужной специалиацией.

Например:

C++
1
2
3
4
5
6
template<typename T>
void foo(T* obj) {}
 
vector<Base*> vec;
vec.push_back(Derived);
foo(vec[0]); // Это все преобразовывается в foo<Derived*>(vec[0]);
Но как я понял, так сделать никак не получится. Шаблонная функция никак не может вытащить наследника из базового типа
0
4 / 4 / 0
Регистрация: 27.05.2017
Сообщений: 26
16.08.2017, 22:49 8
Цитата Сообщение от Kertis138 Посмотреть сообщение
Но как я понял, так сделать никак не получится. Шаблонная функция никак не может вытащить наследника из базового типа
Ну попробуй vector<tuple>, или как я предложил до этого бинарное дерево.

P.S. реализовать можно все что угодно, главное что бы было желание.
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 335
16.08.2017, 23:01  [ТС] 9
Цитата Сообщение от TerroJames Посмотреть сообщение
Ну попробуй vector<tuple>
C++
1
std::vector<std::tuple<Base>> vec;  //Опять же, вывести Derived из Base не получится
Добавлено через 1 минуту
Цитата Сообщение от TerroJames Посмотреть сообщение
P.S. реализовать можно все что угодно, главное что бы было желание.
Можно. У меня получилось очень сильно извратиться через std::type_index и сложные контейнеры... Вышло 300+ строк.
Но "побаловались и хватит". Такие велосипеды не нужны. Дело в архитектуре... не верно я подошел к задаче.
0
Эксперт С++
8739 / 4317 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
16.08.2017, 23:04 10
Лучший ответ Сообщение было отмечено Kertis138 как решение

Решение

Цитата Сообщение от Kertis138 Посмотреть сообщение
Шаблонная функция никак не может вытащить наследника из базового типа
шаблоны - это такие маленький волшебники.

следующий код вычисляет фактические типы наследников,
и выбирает соответствующую типам перегрузку:

http://rextester.com/MZQCSN52990

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <boost/variant.hpp>
 
#include <iostream>
using namespace std;
 
 
struct line {};
 
struct arc {};
 
struct intersects_algo : boost::static_visitor<bool> {
    bool operator()( const line& l1, const line& l2 ) const {
        cout<<"line vs line\n"; return true;
    }
 
    bool operator()( const arc& a1, const arc& a2 ) const {
        cout<<"arc vs arc\n"; return true;
    }
 
    bool operator()( const line& l, const arc& a ) const {
        cout<<"line vs arc\n"; return true;
    }
    bool operator()( const arc& a, const line& l ) const {
        return this->operator()( l, a );
    }
};
 
using element = boost::variant<line, arc>;
 
bool intersects( const element& e1, const element& e2 ) {
    return boost::apply_visitor(intersects_algo(), e1, e2 );
}
 
 
int main() {
 
    element e1 = line{};
    element e2 = arc{};
 
    bool result = intersects(e1, e2);
 
    return 0;
}
другое дело что вот это:
Цитата Сообщение от Kertis138 Посмотреть сообщение
foo(vec[0]); // Это все преобразовывается в foo<Derived*>(vec[0]);
очень сильно попахивает проблемой XY

вы хотите что то не правильное.
инфа 100%
1
73 / 69 / 38
Регистрация: 09.10.2012
Сообщений: 238
16.08.2017, 23:21 11
Храните в векторе указатели и используйте не специализацию шаблона, а виртуальные функции.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Base1 {
    virtual std::string get_type() const
    {
        return "Base1";
    }
};
class Base2 : public Base1 {
    virtual std::string get_type() const
    {
        return "Base2";
    }
};
class Base3 : public Base2 {
    virtual std::string get_type() const
    {
        return "Base3";
    }
};
 
void foo(const Base1 *obj)
{
    std::cout << obj->get_type() << std::endl;
}
 
std::vector<Base1*> vec;
 
int main()
{
    vec.push_back(new Base1());
    vec.push_back(new Base2());
    vec.push_back(new Base3());
    foo(vec[0]);
    foo(vec[1]);
    foo(vec[2]);
 
    // не забываем освободить память (или можно использовать умные указатели)
}
1
1550 / 875 / 179
Регистрация: 05.12.2015
Сообщений: 2,555
16.08.2017, 23:33 12
Цитата Сообщение от Kertis138 Посмотреть сообщение
C++
1
2
3
std::vector<Base1> vec;
vec.push_back(Base1());
vec.push_back(Base2());
Цитата Сообщение от Kertis138 Посмотреть сообщение
контейнер хранить любого наследника некоторого базового класса.
Нет. В данном случае вектор хранит только Base1. Когда вы делаете push_back(Base2()) Base2() неявно кастится к Base1.
Шаблоны - не серебрянная пуля. И они работают на этапе компиляции. А у вас тип определяется в рантайме. Ну так и используйте прямо предназначенные для этого средства языка (виртуальные функции). В векторе можно хранить shared_ptr, например.
1
6 / 6 / 1
Регистрация: 25.02.2016
Сообщений: 335
17.08.2017, 11:50  [ТС] 13
Нашел приемлемое решение через CRTP.
Получился вот такой код:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
template<class T>
void foo();
 
class Base
{
public:
    virtual ~Base() = default;
    virtual void dispatch() = 0;
};
 
template <class T>
class BaseTml : public Base
{
public:
    void dispatch() override
    {
        foo<T>();
    }
};
 
 
 
class A : public BaseTml<A>
{
};
 
class B : public BaseTml<B>
{
};
 
 
template<>
void foo<A>()
{
    std::cout << "A" << std::endl;
}
 
template<>
void foo<B>()
{
    std::cout << "B" << std::endl;
}
USAGE
C++
1
2
3
4
5
    std::vector<std::shared_ptr<Base>> vec;
    vec.emplace_back(std::make_shared<A>());
    vec.emplace_back(std::make_shared<B>());
    vec[0]->dispatch(); //Вызовет Foo<A>
    vec[1]->dispatch(); //Вызовет Foo<B>
0
17.08.2017, 11:50
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
17.08.2017, 11:50
Помогаю со студенческими работами здесь

Определение нужного типа
Всем привет Есть такая небольшая задачка на сообразительность) Нужно обобщить тип передаваемого...

Определение введенного типа
Задался вопросом как сделать так чтоб программа определила вводимый тип. Тоесть программа требует...

Определение типа окна
Добрый день! А подскажите пжалста, перебираю контролы чужого окна и мне надо выбрать только поля...

Определение типа треугольника
подскажите пожалуйста как узнать в программе тип треугольника? Вот код рабочий.#include &lt;iostream&gt;...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
13
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru