|
Программирование >> Включение нужных заголовков
Более того, если бы наш мир был действительно идеальным, алгоритм for each мог бы использоваться и для вызова Widget: :test в контейнере указателей Widget*: list<Widget*> Ipw: Список Ipw содержит указатели на объекты Widget. for each(lpw.begin(),lpw.end(), Вариант 3 (не компилируется!) &Widget::test): Но подумайте, что должно было бы происходить в этом идеальном мире. Внутри функции for each в варианте 1 вызывается внешняя функция, поэтому должен использоваться синтаксис 1. Внутри вызова for each в варианте 2 следовало бы использовать синтаксис 2, поскольку вызывается функция класса. А внутри функции foreach в варианте 3 пришлось бы использовать синтаксис 3, поскольку речь идет о функции класса и указателе на объект. Таким образом, нам понадобились бы три разных версии foreach - разве такой мир можно назвать идеальным? В реальном мире сушествует только одна версия for each. Нетрудно представить себе возможную ее реализацию: tempiate<typename Inputlterator.typename Function> Function for each(Input Iterator begin. Inputlterator end, Function f) while (begin!=end)f(*begin++): Жирный шрифт используется для выделения того, что при вызове foreach используется синтаксис 1. В STL существует всеобщее правило, согласно которому функции и объекты функций всегда вызываются в первой синтаксической форме (как внешние функции). Становится понятно, почему вариант 1 компилируется, а варианты 2 и 3 не компилируются - алгоритмы STL (в том числе и for each) жестко закодированы на использование синтаксиса внешних функций, с которым совместим только вариант 1. Теперь понятно, для чего нужны функции memfun и niein fun ref. Они обеспечивают возможность вызова функций классов (обычно вызываемых в синтаксисе 2 и 3) при помощи синтаксиса 1. Принцип работы mein fun и mein fun ref прост, хотя для пущей ясности желательно рассмотреть объявление одной из этих функций. В действительности они представляют собой шаблоны функций, причем существует несколько вариантов mem fun и mein fun ref для разного количества параметров и наличия-отсутствия константности адаптируемых ими функций классов. Одного объявления вполне достаточно, чтобы разобраться в происходящем: tempiate<typename R, typename C> Объявление mem fun для неконстантных mem fun t<R.C> функций без параметров. С - класс. nieni fun(R(C: :*pmf)()): R - тип возвращаемого значения функции, на которую ссылается указатель Функция niein f un создает указатель pmf на функцию класса и возвращает объект типа niein fun t. Тип представляет собой класс функтора, содержащий указатель на функцию и функцию operatorO, которая по указателю вызывает функцию для объекта, переданного operatorO. Например, в следующем фрагменте: list<Widget*> Ipw: См. ранее for each(1pw.begi n(). 1 pw.end(), meni fun(&Widget: :test)); Теперь нормально компилируется При вызове for each передается объект типа niein fun t, содержащий указатель на Wi dget:: test. Для каждого указателя Wi dget* в 1 pw алгоритм f or each вызывает объект mein fun t с использованием синтаксиса 1, а этот объект непосредственно вызывает Widget; :test для указателя Widget* с использованием синтаксиса 3. В целом niein fun приводит синтаксис 3, необходимый для Widget: :test при использовании с указателем Widget* к синтаксису 1, используемому алгоритмом for each. По вполне понятным причинам такие классы, как inein f unt, называются адаптерами объектов функций. Наверное, вы уже догадались, что по аналогии со всем, о чем говорилось ранее, функции memf unref адаптируют синтаксис 2 к синтаксису 1 и генерируют адаптеры типа memfunreft. Объекты, создаваемые функциями mein fun и memfunref, не ограничиваются простой унификацией синтаксиса для компонентов STL. Они (а также объекты, создаваемые функцией ptrf un) также предоставляют важные определения типов. Об этих определениях уже было рассказано в совете 40, поэтому я не стану повторяться. Тем не менее, стоит разобраться, почему конструкция for each(vw.begin(),vw.endO,test): См. ранее, вариант 1. Нормально компилируется компилируется, а следующие конструкции не компилируются: for each(vw.begin().vw.end().&Widget::test); См. ранее, вариант 2. Не компилируется. for each(lpw.begin(),lpw.endO,&Widget::test): См. ранее, вариант 3. Не компилируется При первом вызове (вариант 1) передается настоящая функция, поэтому адаптация синтаксиса вызова для for each не нужна; алгоритм сам вызовет ее с правильным синтаксисом. Более того, foreach не использует определения типов, добавляемые функцией ptr fun, поэтому при передаче test функция ptr fun не нужна. С другой стороны, добавленные определения не повредят, поэтому следующий фрагмент функционально эквивалентен приведенному выше: for each(vw.begin(),vw.end(),ptr fun(test)): Компилируется и работает. как вариант 1. Если вы забываете, когда функция ptr fun обязательна, а в каких случаях без нее можно обойтись, лзше используйте ее при всех передачах функций компонентам STL. STL игнорирует лишние вызовы, и они не отражаются на быстродействии программы. Возможно, во время чтения вашей программы кто-нибудь удивленно поднимет брови при виде лишнего вызова ptr f un. Насколько это беспокоит вас? Наверное, ответ зависит от природной мнительности. Существует и другой подход - использовать ptr fun в случае крайней необходимости. Если функция отсутствует там, где необходимы определения типов, компилятор выдает сообщение об ошибке. Тогда вы возвращаетесь к программе и включаете в нее пропущенный вызов. С iiieni fun и iiiein fun ref ситуация принципиально иная. Эти функции всегда должны применяться при передаче функции компонентам STL, поскольку помимо определения типов (необходимых или нет) они адаптируют синтаксис вызова. Будем считать, что естественная сортировка объектов Widget осуществляется по атрибуту weight, что отражено в операторе < класса Widget: bool operator<(const WidgetS Ihs. const WidgetS rhs) { return 1hs.wei ght()<rhs.wei ght(); Предположим, потребовалось создать контейнер multiset<Widget>, в котором объекты Widget отсортированы по атрибуту maxSpeed. Известно, что для контейнера mu1tiset<Widget> используется функция сравнения less<Widget>, которая по умолчанию вызывает функцию operator< класса Widget. Может показаться, что единственный способ сортировки multiset<Widget> по атрибуту maxSpeed основан на разрыве связи между 1 ess<Wi dget> и operator< и специализации 1 ess<Wi dget> на сравнении атрибута maxSpeed: templateo Специализация std::less struct std::less<Widget>: для Widget: такой подход public считается крайне нежелательным! std::binary function<Widget. Widget. Базовый класс описан bool>{ в совете 40 bool operatorO (const WidgetS Ihs. const WidgetS rhs) const который обычно используется для функций класса, к синтаксису, принятому в STL. Если не использовать эти функции при передаче указателей на функции класса, программа не будет компилироваться. Остается лишь разобраться со странными именами адаптеров. Перед нами самый настоящий пережиток прошлого STL. Когда впервые возникла необходимость в адаптерах, разработчики STL ориентировались на контейнеры указателей (с учетом недостатков таких контейнеров, описанных в советах 7,20 и 33, это может показаться странным, но не стоит забывать, что контейнеры указателей поддерживают полиморфизм, а контейнеры объектов - нет). Когда понадобился адаптер для функций классов (МЕМЬег FUNctions), его назвали niein fun. Только позднее разработчики поняли, что для контейнеров объектов понадобится другой адаптер, и для этой цели изобрели имя niein f un ref. Конечно, выглядит не слишком элегантно, но... бывает, ничего не поделаешь. Пусть тот, кому никогда не приходилось жалеть о поспешном выборе имен своих компонентов, первым бросит камень. Совет 42. Следите за тем, чтобы конструкция less<T> означала operator< Допустим, объект класса Widget обладает атрибутами weight и maxSpeed: class Widget { public: size t weight О const: size t maxSpeedО const:
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |