Использование библиотеки STL

Проблемы с компиляцией программ под STL
и их красивое решение в GNU C++

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

Пусть мы передаём vector<int> как const reference параметр (как это и следует делать) в некую функцию. А в этой функции мы работаем с этим массивом посредством итераторов:

void f(const vector<int>& v) {
    for(
	    vector<int>::iterator it = v.begin(); 
        // ну что же здесь не так
        ...
        ...
}
В этом коротком участке кода есть ошибка. Вы можете её обнаружить?

Дело в том, что из немодифицируемого (const) объекта мы пытаемся получить модифицируемый (non-const) итератор при помощи функции begin(). Подобное преобразование является недопустимым. Корректный код выглядит следующим образом:

void f(const vector<int>& v) {
    int r = 0;
    // используется const_iterator
    for(vector<int>::const_iterator it = v.begin(); 
                                  it != v.end(); it++) {
        r += (*it) * (*it);
    }
    return r;
}
В свете всего вышесказанного, имеет смысл знать про одну очень интересную функцию компилятора GNU C++. Заметим, что данная функция не входит в cтандарт C++, поэтому в большинстве других компиляторов её нет.

Итак, «оператор» typeof(x). На этапе компиляции он заменяется на тип выражения в скобках. Пример:

typeof(a+b) x = (a+b);
Переменная x будет иметь тип, соответствующий типу выражения a+b. Напомним, что typeof(c.size()) -- unsigned int для всех контейнеров STL. Но наибольшую пользу можно извлечь из typeof в следующем контексте:
#define tr(container, iterator) \
  for(typeof(container.begin()) iterator=container.begin(); \
  it != container.end(); it++)
Данный макрос -- сокращение от traverse -- будет работать даже для самого сложного типа контейнера, независимо от того, каким образом этот контейнер к моменту использования определён. Для const-объектов он породит const_iterator'ы:
void f(const vector<int>& v) {
   int r = 0;
   tr(v, it) {
      r += (*it)*(*it);
   }
   return r;
}

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

void operate(const map< set< pair<int,int> >, 
  vector< pair< double, pair<int,int> > > > &object) {
   for( ... ) // MAMMA MIA!
}

Хотя, например, такая операция, как подсчитать сумму всех double из правой части map с использованием макроса tr выполняется сравнительно просто:

void operate(const map< set< pair<int,int> >, 
  vector< pair< double, pair<int,int> > > > &c) {
   double result = 0;
      tr(c, it) {
         tr(it->second, it2) {
            result += it2->first;
         }
      }
   return result;
}