воскресенье, 8 июня 2008 г.

5 программ

Получил эстафету от Юрия Волкова, цель которой, перечислить 5 самых нужных для работы прграмм. Жаль конечно что только 5, так как использую я намного больше, но все-же: 1. Visual Studio 2005 professional - главный рабочий инструмент. 2. Total Commander, не ice (точнее не FAR), но мне нравится)) 3. Subversion, не hg, не git и не bazaar, а старый добрый svn, надеюсь пощупать версию 1.5 :) 4. Firefox, с помощью которого я читаю почту, узнаю новости, гуглю и пишу этот блог ;) 5. gpodder, по пути на работу я иногда слушаю подкасты, а gpodder неплохо мне в этом помогает. А кому передать эстафету я не знаю, так как без понятия кто мнея читает так что передаю ее тебе :-)

пятница, 6 июня 2008 г.

Опять шаблоны

Я люблю С++ за то, что код делающий что-то простое может быть сколь угодно простым, но в то-же время сколь угодно сложным. Например нужно написать функцию добавляющую элемент в контейнер, но конкретный тип контейнера неизветсен, вот простой вариант:
#include <iostream>
#include <vector>

template <class C, class T>
void add (T inserter, C value)
{
    *inserter = value;
}


int main(int argc, char *argv[])
{
    std::vector <int> arr;
    add(std::inserter(arr, arr.begin()), 1);
    add(std::inserter(arr, arr.begin()), 2);
    add(std::inserter(arr, arr.begin()), 3);
    for (int i = 0; i != arr.size(); i++)
    {
        std::cout << arr[i] << std::endl;
    }
    system("pause");
}
Функция add получает 2 параметра, первый должен быть insert_iterator-ом, второй - значение которое добавляется в контейнер. Что здесь не так? Тип первого параметра может быть любым, можно например засунуть туда указатель на int и все скомпилируется, но будет неправильно работать. По сигнатуре функции непонятно что она должна получать, указатель на int, или все-же insert_iterator (это не очень понятно и по коду функции (: Если объяснить компилятору более доходчиво, то получится что-то вроде этого:
#include <iostream>
#include <vector>
#include <list>

template < 
    template <class T, class A> class container,
    template <class T> class allocator,
    class Arg
>
void add ( std::insert_iterator< container<Arg, allocator<Arg> > > inserter, Arg value ) 
{
    *inserter = value;
}

int main(int argc, char *argv[])
{
    std::vector <int> arr;
    add(std::inserter(arr, arr.begin()), 1);
    add(std::inserter(arr, arr.begin()), 2);
    add(std::inserter(arr, arr.begin()), 3);
    for (int i = 0; i != arr.size(); i++)
    {
        std::cout << arr[i] << std::endl;
    }
    system("pause");
}
Этот код немного сложнее чем предыдущий. Функция add работает так-же, но имеет теперь три шаблонных параметра а не два, причем первые два - шаблонные(шаблонные параметры) . И теперь указатель на int туда передать нельзя, что очень хорошо. Уверен что можно усложнить этот пример еще и даже получить из этого какую-то пользу, возможности С++ безграничны :)) Update В этом коде есть небольшая ошибка, в функцию add, insert_iterator нужно передавать по ссылке, если передавать по значению, то будут проблемы с вектором. Дело в том, что после перераспределения памяти, итератор, который передается в add не изменится, а изменится только его локальная копия. Это приведет к тому, что итератор, указывающий на начало вектора, который хранится внутри insert_iterator-a станет показывать в неизвестность (пример работает потому, что inserter создается для каждого вызова add заново). Для list и deque это неактуально, так как там insert не приводит к порче итераторов.