Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Для чего нужны шаблоны в C++ 
:(
    Опции темы
Alex007
  Дата 29.12.2005, 23:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 6
Регистрация: 29.12.2005

Репутация: нет
Всего: нет



Скажите, для чего нужны шаблоны в C++ и в кратце пожалуйста объясните их принцып действия,
а ато читая книги по С++ я так и не понял шаблоны smile smile smile smile smile smile smile smile smile smile
PM MAIL   Вверх
blackofe
Дата 30.12.2005, 00:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 173
Регистрация: 29.11.2005

Репутация: 4
Всего: 4



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

Код
const int f(const int t1, const int t2)
{
    return t1+t2;
}

const long f(const long t1, const long t2)
{
    return t1+t2;
}

const float f(const float t1, const float t2)
{
    return t1+t2;
}

const double f(const double t1, const double t2)
{
    return t1+t2;
}

int main()
{
    int i1 = 2, i2 = 3;
    long l1 = 23, l2 = 34;
    float f1 = 2.34f, f2 = 3.45f;
    double d1 = 13.245, d2 = 12e-2;

    cout << f(i1, i2) << endl;
    cout << f(l1, l2) << endl;
    cout << f(f1, f2) << endl;
    cout << f(d1, d2) << endl;

    return 0;
}


ты используешь шаблон:

Код
template<class T>
T f(T t1, T t2)
{
    return t1+t2;
}

int main()
{
    int i1 = 2, i2 = 3;
    long l1 = 23, l2 = 34;
    float f1 = 2.34f, f2 = 3.45f;
    double d1 = 13.245, d2 = 12e-2;

    cout << f<int>(i1, i2) << endl;
    cout << f<long>(l1, l2) << endl;
    cout << f<float>(f1, f2) << endl;
    cout << f<double>(d1, d2) << endl;

    return 0;
}


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

Код

    string s1("1234"), s2("5678");
    cout << f<string>(s1, s2) << endl;


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

Код
struct A {
    int n;
    A() {}
    A(const int N) : n(N) {}

    const A operator +(const A &a) const        {    A a0(*this);
                                                    a0.n += a.n;
                                                    return a0; }
};

//...

    A a1(77), a2(88);
    A a3 = f<A>(a1, a2);
    cout << a3.n << endl;


это что касается шаблонов функций.
PM MAIL   Вверх
blackofe
Дата 30.12.2005, 02:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 173
Регистрация: 29.11.2005

Репутация: 4
Всего: 4



шаблоны классов интереснее.

Код
template<class T>
struct B {
    T t;
    B() {}
    B(T t0) : t(t0) {}

    const T GetT() const                        {    if(typeid(T) == typeid(int))
                                                        return t*2;
                                                    else
                                                        return t; }
};

int main()
{
    cout << B<int>(12).GetT() << endl;
    cout << B<long>(12).GetT() << endl;

    return 0;
}


программа выведет:

Код

24
12


потому что в первом случае инстанциирован класс для типа int.

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

как пример, стандартная библиотека с++ (ее еще называют stl - standard template library), построенная процентов на 90 на шаблонах, имеет шаблон vector. с его помощью можно построить коллекцию-вектор, допустим, int'ов, а можно string'ов, а можно любого другого пользовательского типа. и ко всем этим коллекциям можно применять общие операции добавления элемента, извлечения и пр.

такой код:

Код
#include <vector>
using namespace std;

struct A {
    int n;
    A() {}
    A(const int N) : n(N) {}

    const A operator +(const A &a) const        {    A a0(*this);
                                                    a0.n += a.n;
                                                    return a0; }
    friend ostream &operator <<(ostream &ost, const A &a)
                                                {    return ost << "n = " << a.n; }
};

int main()
{
    vector<int> vi;
    vi.push_back(1);
    vi.push_back(4);

    vector<string> vs;
    vs.push_back("s123");
    vs.push_back("something");
    vs.push_back("bla-bla-bla");

    vector<A> va;
    va.push_back(A(100));
    va.push_back(A(9999));

    for(vector<int>::const_iterator cit = vi.begin(); cit < vi.end(); ++cit)
        cout << *cit << endl;

    for(vector<string>::const_iterator cit = vs.begin(); cit < vs.end(); ++cit)
        cout << *cit << endl;

    for(vector<A>::const_iterator cit = va.begin(); cit < va.end(); ++cit)
        cout << *cit << endl;

    return 0;
}


выведет:

Код
1
4
s123
something
bla-bla-bla
n = 100
n = 9999


коллекции vi, vs, va содержат элементы разной природы, но работать с ними можно общими принципами. что, в общем-то, довольно удобно.
PM MAIL   Вверх
MAKCim
Дата 30.12.2005, 09:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 52
Всего: 207



Цитата

Код

const T GetT() const                        {    if(typeid(T) == typeid(int))
                                                        return t*2;
                                                    else
                                                        return t; }



ИМХО, код немного неправильный, т. к если T!=int и T-пользовательский тип, будет попытка вызвать T::operator*(int), которого в T может не быть, т.е в общем случае код всегда работает правильно только для примитивных типов


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
Void
Дата 30.12.2005, 11:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



MAKCim
+1. Здесь лучше применить специализацию шаблонов.
Хотя и сам пример какой-то надуманный.


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
blackofe
Дата 30.12.2005, 21:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 173
Регистрация: 29.11.2005

Репутация: 4
Всего: 4



Цитата(MAKCim @ 30.12.2005, 09:36)
ИМХО, код немного неправильный, т. к если T!=int и T-пользовательский тип, будет попытка вызвать T::operator*(int), которого в T может не быть, т.е в общем случае код всегда работает правильно только для примитивных типов

не понял, что тут неправильного? проверка

Код

typeid(T) == typeid(int)


сработает для любого типа T. и если T - не int, то мы перейдем к инструкции

Код

return t;


имхо, никакого криминала.
Добавлено @ 21:15
Цитата(Void @ 30.12.2005, 11:55)
MAKCim
+1. Здесь лучше применить специализацию шаблонов.
Хотя и сам пример какой-то надуманный.

насчет надуманности согласен. однако пример был дан не как образец применения, а как образец написания. т.е. типа "что будет, если использовать шаблон?"

и между прочим сам думал, какой бы пример придумать, чтобы было видно, что без шаблонов класса не обойтись. и кроме как для использования в коллекциях ничего путного не придумал (кстати, что ниже и продемонстрировал). в дебри типа ATL или WTL пускаться не хотелось. может быть, кто-нибудь из мэтров восполнит этот мой пробел? smile

Это сообщение отредактировал(а) blackofe - 30.12.2005, 21:16
PM MAIL   Вверх
blackofe
Дата 30.12.2005, 21:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 173
Регистрация: 29.11.2005

Репутация: 4
Всего: 4



Цитата(MAKCim @ 30.12.2005, 09:36)
ИМХО, код немного неправильный, т. к если T!=int и T-пользовательский тип, будет попытка вызвать T::operator*(int), которого в T может не быть, т.е в общем случае код всегда работает правильно только для примитивных типов

и во-вторых, что значит неправильный? понятно, что шаблоны не всегда могут предусмотреть все варианты типов, с которыми эти шаблоны будут использоваться. даже та же stl этим грешит. и, имхо, это нормально.

по поводу грешит. попробуй поместить в vector объектов объект, имеющий приватный конструктор копии.

Код

#include <vector>
using namespace std;

struct A {
    int n;
    A() {}
    A(const int N) : n(N) {}

    const A operator +(const A &a) const        {    A a0(*this);
                                                    a0.n += a.n;
                                                    return a0; }
    friend ostream &operator <<(ostream &ost, const A &a)
                                                {    return ost << "n = " << a.n; }
private:
    A(const A &) {}
};

int main()
{
    vector<A> va;
    A a1(100);
    va.push_back(a1);
    // or
    va.push_back(A(100));
    return 0;
}


ты получишь error C2248: 'A::A' : cannot access private member declared in class 'A'. другими словами, шаблон vector не работает "в общем случае". но ведь мы же не кричим при этом, что код стандартной библиотеки c++ "немного неправильный". smile
PM MAIL   Вверх
Void
Дата 30.12.2005, 23:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



blackofe
Если T != int, то твой код в compile-time требует определенного T::operator *(int), хотя по логике программы он совсем не нужен.


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
MAKCim
Дата 31.12.2005, 09:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 52
Всего: 207



При инстанцировании класса компилятор проходит по всем веткам, а не
Цитата

не понял, что тут неправильного? проверка
typeid(T) == typeid(int)
сработает для любого типа T. и если T - не int, то мы перейдем к инструкции
return t;
имхо, никакого криминала.

я говорил про это
Код

#include <typeinfo>

template<class T> class A
{
public:
    T F(T obj)
    {
        if (typeid(T)==typeid(int)) return obj*7;
        else return obj;
    }
};

class C {};           //любой пользовательский тип, в котором не определен operator*(int)

int main()
{
    A<int> a;
    B<C> b;
    a.F(19);
    b.F(C());          //ошибка, т . е компилятор не пропускает ветку return obj*7
    return 0;
}



--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
Janus
Дата 31.12.2005, 16:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 186
Регистрация: 26.9.2005

Репутация: нет
Всего: 1



Цитата(blackofe @ 30.12.2005, 00:35)
Код
template<class T>
T f(T t1, T t2)
{
    return t1+t2;
}

int main()
{
    int i1 = 2, i2 = 3;
    long l1 = 23, l2 = 34;
    float f1 = 2.34f, f2 = 3.45f;
    double d1 = 13.245, d2 = 12e-2;

    cout << f<int>(i1, i2) << endl;
    cout << f<long>(l1, l2) << endl;
    cout << f<float>(f1, f2) << endl;
    cout << f<double>(d1, d2) << endl;

    return 0;
}

У меня вопрос. Чем различаются вызовы из вышеприведенного примера:
Код

int k;
k = f<int>(1, 2);

И такой вызов:
Код

int k;
k = f(1, 2);

Оба работают как и положено, так какая разница? Что лучше?
--------------------
Проект "Репликатор" 
PM MAIL   Вверх
Void
Дата 31.12.2005, 17:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


λcat.lolcat
****


Профиль
Группа: Участник Клуба
Сообщений: 2206
Регистрация: 16.11.2004
Где: Zürich

Репутация: 40
Всего: 173



Цитата(Janus @ 31.12.2005, 18:50)
Оба работают как и положено, так какая разница? Что лучше?

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


--------------------
“Coming back to where you started is not the same as never leaving.” — Terry Pratchett
PM MAIL WWW GTalk   Вверх
Janus
Дата 31.12.2005, 20:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 186
Регистрация: 26.9.2005

Репутация: нет
Всего: 1



А когда будет разница? Зачем тогда нужна такая форма записи k = f<int>(1, 2);?

В одном учебнике такой формы нет вообще, а в другом написано, что функцию сначала надо объявить с конкретным указанием типа, а уже потом использовать...
--------------------
Проект "Репликатор" 
PM MAIL   Вверх
DeadSoul
Дата 31.12.2005, 21:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Участник
Сообщений: 1217
Регистрация: 25.9.2005
Где: Москва

Репутация: 2
Всего: 11



Цитата(Janus @ 31.12.2005, 20:45)
А когда будет разница? Зачем тогда нужна такая форма записи k = f<int>(1, 2);?

Иногда компилятор не в состояния "автоматом" определить тип аргумента.

Цитата(Janus @ 31.12.2005, 20:45)
В одном учебнике такой формы нет вообще

выкинь эту книгу


Цитата(Janus @ 31.12.2005, 20:45)
функцию сначала надо объявить с конкретным указанием типа, а уже потом использовать...

это не функция. Это шаблон. Естественно, шаблон должен быть полностьь "виден" в месте использования


--------------------
 Если Вы получили ответ на Ваш вопрос, то нажмите на "Вопрос решен". 

Бьем спамеров их же оружием. Пусть весь спам сыпется им
[email protected] 
PM   Вверх
LPBOY
Дата 31.12.2005, 21:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 228
Регистрация: 12.7.2005

Репутация: 20
Всего: 20



Если вызвать так:
Код

int i = 1;
double d = 2.0;
f(i, d);

то будет ошибка - неоднозначность.
Поэтому в этом случае надо уточнить
Код

f<int>(i, d);
//или
f<double>(i, d);


Или такой случай:
Код

template<class T>
struct A
{
    typedef T type;
};

template<class T>
void f(typename A<T>::type a)
{
}

int main()
{
    int i;
    f(i);
    return 0;
}

В функции f невозможно вывести тип параметра шаблона, а если уточнить f<int>,
то все ok.
--------------------
Каждый человек по-своему прав, а по-моему нет...
PM MAIL   Вверх
Janus
Дата 1.1.2006, 18:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 186
Регистрация: 26.9.2005

Репутация: нет
Всего: 1



LPBOY, спасибо, понял.
--------------------
Проект "Репликатор" 
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0681 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.