Обратные вызовы в C++ | страница 81



Листинг 85. Использование адресного распределения

>struct FO

>{

>  int operator() (int eventID)

>  {

>    return 10;

>  }

>};


>int ExternalHandler(int eventID)

>{

>  return 0;

>}


>struct ReceiverAddress  // (1)

>{

>  ReceiverAddress(int idGroup = 0, int idNumber = 0)

>  {

>    group = idGroup; number = idNumber;

>  }


>  int group;

>  int number;

>};


>template<>

>struct std::less  // (2)

>{

>  bool operator() (const ReceiverAddress& addr1, const ReceiverAddress& addr2) const

>  {

>    if (addr1.group < addr2.group)

>    {

>      return true;

>    }

>    else

>    {

>      if (addr1.group == addr2.group)

>        return addr1.number < addr2.number;

>      else

>        return false;

>      }

>  }

>};


>int main()

>{

>  int eventID = 0;

>  FO fo;

>  auto lambda = [](int eventID) { return 0; };


>  AddressDistributor, int(int)> distributor;  // (3)


>  distributor.addReceiver({ 1,1 }, fo);               // (4)

>  distributor.addReceiver({ 2,2 }, ExternalHandler);  // (5)

>  distributor.addReceiver({ 3,3 }, lambda);           // (6)


>  distributor({ 1,1 }, eventID);  // (7)

>  distributor({ 2,2 }, eventID);  // (8)

>  distributor({ 3,3 }, eventID);  // (9)

>}


В строке 1 объявлена структура для адреса, которая состоит из двух полей: идентификатор группы и номер получателя в группе. Сравнить эти две структуры напрямую нельзя, поэтому потребуется реализовать предикат.

В строке 2 объявлен функциональный объект, реализующий предикат для сравнения адресов. Почему именно в таком виде? Дело в том, что std::map требует, чтобы в качестве предиката использовался именно функциональный объект, мы не можем для этого использовать внешнюю функцию или лямбда-выражение. Это связано с тем, что в контейнере предикат хранится в виде переменной с конструктором, тип переменной определяется параметром шаблона. А наличие конструктора может обеспечить только функциональный объект.

Указанный подход имеет как достоинства, так и недостатки. С одной стороны, нам достаточно всего лишь объявить тип объекта в параметре шаблона, а затем про него можно забыть. Объект не нужно ни настраивать, ни передавать в конструктор как входной аргумент. С другой стороны, было бы удобно использовать в качестве предиката что-либо другое, например, лямбда-выражение или внешнюю функцию. Но в этом случае предикат пришлось бы инициализировать в конструкторе, причем ему нельзя было бы назначить значение по умолчанию. В любом случае, мы вынуждены следовать заданной реализации, поэтому предикат объявляем как функциональный объект.