С наступающим Новым годом! Форум программистов, компьютерный форум, киберфорум
Наши страницы
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.75/4: Рейтинг темы: голосов - 4, средняя оценка - 4.75
dimcoder
Полярный
468 / 441 / 157
Регистрация: 11.09.2011
Сообщений: 1,144
1

Свой тип как параметр шаблона

17.03.2016, 22:01. Просмотров 703. Ответов 17
Метки нет (Все метки)

Есть вопрос по коду:
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
#include <iostream>
#include <memory>
#include <vector>
 
using namespace std;
 
template<class T>
class Base
{
    vector<unique_ptr<T>> v;               //Здесь храню классы унаследованные от Base
};
 
class GoodChild: public Base<int>   //Работает
{
 
};
 
class BadChild: public Base<class Base>    //Не работает
{
 
};
 
int main()
{
    return 0;
}
Можно ли заставить компилятор все же унаследовать? Ведь такой код работает:
C++
1
2
3
4
class Base
{
    vector<unique_ptr<Base>> v;
};
Если нет, то что можно сделать, что бы работало? Сделать специализацию класса не получается, но не писать же отдельный класс!
0
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
17.03.2016, 22:01
Ответы с готовыми решениями:

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

Как сконвертировать свой тип в тип double?
Можно ли конвертировать свой тип в тип doble? Если да, то каким способом?

Шаблоннный клас как параметр шаблона
Если в кратце: Имеется такой код: typedef ByteStuffing&lt;USART0,...

Передача функции как параметр шаблона
Хочу передавать в шаблон любую функцию без параметров и вызывать ее из него....

Указатель на статический массив как параметр шаблона
Добрый день. Необходимо параметр шаблона сделать указателем и передавать в...

17
TheCalligrapher
С чаем беда...
Эксперт CЭксперт С++
4838 / 2483 / 695
Регистрация: 18.10.2014
Сообщений: 4,291
17.03.2016, 22:16 2
Цитата Сообщение от dimcoder Посмотреть сообщение
C++
1
class BadChild: public Base<class Base>

Что такое class Base? У вас в программе нет никакого class Base. У вас есть только шаблон class Base<>, требующий аргумента.

И зачем вы пытаетесь передать class Base в тот же самый class Base в качестве параметра шаблона. В чем тут идея?

Цитата Сообщение от dimcoder Посмотреть сообщение
Если нет, то что можно сделать, что бы работало?
"Что можно сделать" зависит от того, что вы хотите получить в результате.

Тут нет телепатов. Поэтому что значит ваш загадочный код, что вы пытаетесь сделать, и что значит "что бы работало" - совершено не ясно.
1
dimcoder
Полярный
468 / 441 / 157
Регистрация: 11.09.2011
Сообщений: 1,144
17.03.2016, 23:21  [ТС] 3
TheCalligrapher, У меня сейчас примерно такая ситуация:
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
44
45
46
47
48
49
50
#include <iostream>
#include <memory>
#include <vector>
 
using namespace std;
 
class Base
{
    protected:
        vector<unique_ptr<Base>> v;
};
 
class C1: public Base
{
};
 
class C2: public Base
{
};
 
class Garry: public Base
{
    void f()
    {
        v.push_back(std::unique_ptr<C1>(new C1));
        v.push_back(std::unique_ptr<C2>(new C2));
    }
    ///Работа с вектором v
};
 
class Tony: public Base
{
    public:
        void addGarry()
        {
            unique_ptr<Garry> newGarry(new Garry);
            v.push_back(std::move(newGarry));
        }
 
        Garry& getGarry(int index)
        {
            return *static_cast<Garry*>(v[index].get());
        }
};
 
 
int main()
{
    return 0;
}
Вот эта конструкция мозолит мне глаза:
C++
1
return *static_cast<Garry*>(v[index].get());
Я знаю что все объекты вектора v - unique_ptr<Garry>. Но при этом каждый раз надо кастовать - неудобно. При этом, если можно было бы сделать так, было бы замечательно:
C++
1
2
3
4
5
6
7
8
9
10
class Tony: public Base<Garry>
{
    public:
        //....
 
        Garry& getGarry(int index)
        {
            return *v[index].get();
        }
};
Но в этом случае ломается Garry.

Я ищу любое решение к проблеме, не обязательно наследованием.

Добавлено через 12 минут
Я тут подумал, меня бы устроило такое решение:
C++
1
2
3
4
5
6
7
8
9
10
11
private:
        std::vector<unique_ptr<Garry>> *vGarry;
 
    public:
        Tony()
        :vGarry(static_cast< std::vector<unique_ptr<Garry>>* >(&v))
        {
 
        }
        //...
}
То есть просто перекастовать сам вектор. Но каст выше не валиден. Что не так?

Добавлено через 21 минуту
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Tony: public Base
{
    public:
 
        Garry& garrify(unique_ptr<Base>& input)
        {
            return *static_cast<Garry*>(input.get());
        }
 
        Garry& getGarry(int index)
        {
            return garrify(v[index]);
        }
};
Это решение мне в прочем подходит. Хотя и не красота... Может есть идеи получше?
0
nonedark2008
1124 / 832 / 249
Регистрация: 28.07.2012
Сообщений: 2,334
17.03.2016, 23:37 4
Цитата Сообщение от dimcoder Посмотреть сообщение
Может есть идеи получше?
Можешь создать себе шаблонную функцию, которая будет кастовать нужный элемент за тебя к нужному типу.
Добавь обработку ошибки преобразования типов.
static_cast -> dynamic_cast
Цитата Сообщение от dimcoder Посмотреть сообщение
То есть просто перекастовать сам вектор
Нет и не советую пытаться. Получиться может, но это будет еще та фигня.

Добавлено через 5 минут
Цитата Сообщение от dimcoder Посмотреть сообщение
Вот эта конструкция мозолит мне глаза
Мне мозолит глаза отсутствие виртуальных деструкторов.
Цитата Сообщение от dimcoder Посмотреть сообщение
Я ищу любое решение к проблеме, не обязательно наследованием.
Отличный вопрос. Что у тебя за проблема?
1
dimcoder
Полярный
468 / 441 / 157
Регистрация: 11.09.2011
Сообщений: 1,144
17.03.2016, 23:52  [ТС] 5
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Добавь обработку ошибки преобразования типов.
Согласен, идея хорошая. Но она мне в данном случае не сильно поможет.
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Нет и не советую пытаться. Получиться может, но это будет еще та фигня.
Чем "еще та фигня" хуже другой написанной фигни?
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Мне мозолит глаза отсутствие виртуальных деструкторов.
И зачем они в данном примере?
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Отличный вопрос. Что у тебя за проблема?
Проблема в том что у некоторых унаследованных классов в векторе будет хранится только один тип данных (унаследованный от Base). С этими конкретными классами одна проблема - нужны постоянные касты к одному и тому же типу.
В других унаследованных классах в этом векторе будет хранится по нескольку разных типов (унаследованных от Base).
Вот я и хочу указать какие классы являются какими - что бы в первом типе классов кастовать не нужно было, а второй тип классов остался таким же.
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Можешь создать себе шаблонную функцию, которая будет кастовать нужный элемент за тебя к нужному типу.
Тоже вариант.
0
nonedark2008
1124 / 832 / 249
Регистрация: 28.07.2012
Сообщений: 2,334
18.03.2016, 01:27 6
Цитата Сообщение от dimcoder Посмотреть сообщение
Чем "еще та фигня" хуже другой написанной фигни?
Возможные утечки памяти и ошибки доступа записи.
Цитата Сообщение от dimcoder Посмотреть сообщение
И зачем они в данном примере?
Утечки или вовсе ошибки при попытке удалить объект через указатель на базовый класс.

dimcoder, тебе нужно продумать что ты с этим всем хочешь делать.
А уже потом строить какие-то иерархии наследования с виртуальными методами.
Пока мне это все видится в виде геттера в базовом классе. А где этого базового геттера будет не хватает, нужно его спрятать под private и сделать свой (который будет обращаться к базовому).
1
dimcoder
Полярный
468 / 441 / 157
Регистрация: 11.09.2011
Сообщений: 1,144
18.03.2016, 03:01  [ТС] 7
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Возможные утечки памяти и ошибки доступа записи.
Если вы про реаллокацию вектора при добавлении элемента, то это не проблема - можно при каждом добавлении элменента обновлять указатель. Других возможных ошибок/утечек я не вижу. Но не суть.
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Утечки или вовсе ошибки при попытке удалить объект через указатель на базовый класс.
Цитата Сообщение от dimcoder Посмотреть сообщение
в данном примере
нету - унаследованные классы не содержат объектов (кроме вектора базового класса, который базовый класс и задестроит). Нет тут никакой потенциальной утечки.
Цитата Сообщение от nonedark2008 Посмотреть сообщение
тебе нужно продумать что ты с этим всем хочешь делать.
А то
Цитата Сообщение от nonedark2008 Посмотреть сообщение
Пока мне это все видится в виде геттера в базовом классе. А где этого базового геттера будет не хватает, нужно его спрятать под private и сделать свой (который будет обращаться к базовому).
Это близко к тому на чем я остановился. Только прятать под private в наследованном классе толку немного - работа идет с указателями базового класса. Того у кого такой указатель есть не остановить. Но т.к. этот геттер будет использоваться только унаследованными классами, то имеет смысл спрятать его под протектед и плясать оттуда. Поэтому можно сказать, что проблема в общем-то решена. Спасибо за помощь!
0
Valeryn
77 / 50 / 16
Регистрация: 17.05.2015
Сообщений: 264
18.03.2016, 05:35 8
C++
1
2
 
class BadChild: public Base<class Base>    //Не работает
Бесконечность!...
class BadChild: public Base<class Base<class Base<class Base<...>>>>

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename T>
class Base
{
    vector<unique_ptr<T>> v;               //Здесь храню классы унаследованные от Base
};
 
class GoodChild: public Base<int>   //Работает
{
};
 
template<typename T>
class BadChild: public Base<class Base<T>>    //FIXED
{
};
 
int main()
{
    return 0;
}
1
dimcoder
Полярный
468 / 441 / 157
Регистрация: 11.09.2011
Сообщений: 1,144
18.03.2016, 16:15  [ТС] 9
Valeryn, интересная идея, но (довольно безнадежная для меня) ошибка компиляции:
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
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <memory>
#include <vector>
 
using namespace std;
 
template <typename T>
class Base
{
    public:
        vector<unique_ptr<Base>> v;
};
 
class C1: public Base<int>
{
};
 
class C2: public Base<int>
{
};
 
template <typename T>
class Garry: public Base<class Base<T>>
{
    void f()
    {
        v.push_back(std::unique_ptr<C1>(new C1));   //v was not declared in this scope
        v.push_back(std::unique_ptr<C2>(new C2));
    }
};
 
template <typename T>
class Tony: public Base<class Garry<T>>
{
    public:
        void addGarry()
        {
            unique_ptr<Garry> newGarry(new Garry<T>);
            v.push_back(std::move(newGarry));
        }
 
        Garry<T>& getGarry(int index)
        {
            return *static_cast<Garry*>(v[index].get());
        }
};
 
 
int main()
{
    return 0;
}
0
hoggy
Заблокирован
Эксперт С++
18.03.2016, 18:53 10
Лучший ответ Сообщение было отмечено dimcoder как решение

Решение

Цитата Сообщение от dimcoder Посмотреть сообщение
Я тут подумал, меня бы устроило такое решение:
C++
1
2
3
4
5
6
class Tony: public Base<Garry>
{
public:
     //....
     Garry& getGarry(int index)     { return *v[index].get();  }
};
http://rextester.com/ACQZ49134

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
44
45
46
47
48
49
50
51
52
53
#include <iostream>
#include <memory>
#include <vector>
 
 
class Base
{
public:
    
    template<class T, class ...Args> void add(Args&&...)
    {
        v.emplace_back( 
            std::make_unique<T>(std::forward<Args>()...) 
        );
    }
    
    template<class T> operator T&()
    {
        return *static_cast<T*>(this);
    }
    
protected:
    std::vector<std::unique_ptr<Base>> v;
};
 
class C1: public Base{};
class C2: public Base{};
 
class Garry: public Base
{
    void f()
    {
        this->template add<C1>();
        this->template add<C2>();
    }
};
 
class Tony: public Base
{
public:
    void addGarry() { this->template add<Garry>(); }
 
    Garry& getGarry(const size_t index)
    {
        return *this->v.at(index);
    }
};
 
 
int main()
{
    return 0;
}
2
dimcoder
Полярный
468 / 441 / 157
Регистрация: 11.09.2011
Сообщений: 1,144
18.03.2016, 20:09  [ТС] 11
hoggy,
пожалуй ваш метод мне нравится. Но с ним можно выстрелить себе в ногу: http://rextester.com/DXXKA34594
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <memory>
#include <vector>
 
 
class Base
{
public:
    
    template<class T, class ...Args> void add(Args&&...)
    {
        v.emplace_back( 
            std::make_unique<T>(std::forward<Args>()...) 
        );
    }
    
    template<class T> operator T&()
    {
        return *static_cast<T*>(this);
    }
    
protected:
    std::vector<std::unique_ptr<Base>> v;
};
 
class C1: public Base{};
class C2: public Base{};
 
class Garry: public Base
{
    void print() { std::cout << "I am Garry#" << y << std::endl; }
    void f()
    {
        this->template add<C1>();
        this->template add<C2>();
    }
    
    float y = 1.5;
};
 
class Tony: public Base
{
public:
    void print() { std::cout << "I am Tony#" << x << std::endl; }
    
    void addGarry() { this->template add<Garry>(); }
    
    Tony& getGarry(const size_t index)
    {
        return *this->v.at(index);
    }
    
    int x = 10;
};
 
 
int main()
{
    Tony tony;
    tony.addGarry();
    tony.getGarry(0).print();
    return 0;
}
Я думаю мне стоит пересмотреть всю архитектуру программы которую я пишу, поэтому милости просим: Предложения по изменению архитектуры
0
hoggy
Заблокирован
Эксперт С++
18.03.2016, 21:51 12
Цитата Сообщение от dimcoder Посмотреть сообщение
Но с ним можно выстрелить себе в ногу
у вас есть два варианта: быстрый static_cast или медленный dynamic_cast
в данной постановке задачи,
других способов поиметь профит не существует в принципе.
1
lemegeton
2935 / 1364 / 467
Регистрация: 29.11.2010
Сообщений: 2,725
18.03.2016, 21:54 13
Очень уж некорректно вы с контейнером работаете.

C++
1
2
3
4
Base& getGarry(const size_t index)
    {
        return *v.at(index);
    }
0
hoggy
Заблокирован
Эксперт С++
18.03.2016, 22:24 14
Цитата Сообщение от lemegeton Посмотреть сообщение
Очень уж некорректно вы с контейнером работаете.
Цитата Сообщение от lemegeton Посмотреть сообщение
Base& getGarry(const size_t index)
мне одному показалось,
что getGarry должен возвращать "Garry", а не "Base"?

фраза: "говорящие имена функций" - вам ни о чем не говорит?
1
lemegeton
2935 / 1364 / 467
Регистрация: 29.11.2010
Сообщений: 2,725
19.03.2016, 13:31 15
Суть(с)(тм) моего сообщения была не в имени метода, а в возвращаемом значении и том, что нельзя так с коллекциями работать. Это архитектурная ошибка.

Когда типизированый указатель помещается в более абстрактный контейнер, информация об исходном типе теряется. Если нужно её восстановить, значит не нужно было её туда класть. Ну или есть ещё множественая диспетчеризация для работы с конкретикой. Паттерн визитёт и т.п. Прямые приведения к наследнику -- выстрел себе в ногу.

dimcoder, попробуйте озвучить исходную задачу. Скорее всего, то, что вы хотите сделать уже известно как делать.
0
dimcoder
Полярный
468 / 441 / 157
Регистрация: 11.09.2011
Сообщений: 1,144
19.03.2016, 13:47  [ТС] 16
lemegeton,
Цитата Сообщение от dimcoder Посмотреть сообщение
Я думаю мне стоит пересмотреть всю архитектуру программы которую я пишу, поэтому милости просим:
Предложения по изменению архитектуры
Решение - хранить два массива shared_ptr как в базовом так и унаследованном классе. Даже думаю тут достаточно иметь vector<unique_ptr> в унаследованном, а в базовом просто хранить vector<Base*>.
0
Valeryn
77 / 50 / 16
Регистрация: 17.05.2015
Сообщений: 264
21.03.2016, 06:27 17
Не пойму в чем у тебя все же за трабл.
main.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "test.h"
 
using namespace std;
 
 
int main()
{
    std::cout << "test 1\n";
    Base<int> test1;
    std::cout << "test 2\n";
    GoodChild<int> test2;
    std::cout << "test 3\n";
    BadChild<int> test3;
 
    return 0;
}
test.h
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
44
#ifndef TEST_H
#define TEST_H
 
#include <iostream>
#include <memory>
#include <vector>
 
template<typename T>
class Base
{
protected:
    std::vector<std::unique_ptr<T>> v;
public:
    Base() {
        std::cout << "i am base\n";
        v.push_back(std::unique_ptr<T>(new T()));
    }
    virtual ~Base() {}
};
 
template<typename T>
class GoodChild: public Base<T>
{
public:
    GoodChild()
    {
        std::cout << "i am good chield\n";
    }
    ~GoodChild() {}
};
 
template<typename T>
class BadChild: public Base<Base<T>>
{
public:
    BadChild()
    {
        std::cout << "i am bad chield\n";
    }
    ~BadChild() {}
};
 
 
#endif // TEST_H
Вывод:
test 1
i am base
test 2
i am base
i am good chield
test 3
i am base
i am base
i am bad chield

при том третий класс отрабатывает как надо, т.к. в вектор badchield кладется новый base, у которого отрабатывает конструктор
0
dimcoder
Полярный
468 / 441 / 157
Регистрация: 11.09.2011
Сообщений: 1,144
24.03.2016, 02:59  [ТС] 18
Valeryn, проблема в том что при росте иерархии классов (в графе) нужно будет в классах повыше указывать все классы ниже: parent_class<Base<Base<Base<Base<Base<Base<Base<Base<int>>>>>>>>. Если что-то нужно поменять или добавить - меняй прототипы всех родительских классов. Брррррр...
Тем не менее, спасибо вам за ответ.
0
24.03.2016, 02:59
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
24.03.2016, 02:59

Результат работы функции как параметр шаблона
В старом с++ билдере такое работало, новый clang ругается. template &lt;class T,...

Как можно изменить параметр шаблона (template)?
есть такие классы: class abstract { public: virtual int method() = 0; //...

Зачем нужен шаблон как параметр шаблона?
Видел много примеров, но что-то не особо понятно зачем и как это работает.


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

Или воспользуйтесь поиском по форуму:
18
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru