Д. Вандевурд, Н.М. Джосаттис - Шаблоны C++. Справочник разработчика (2003) (1160769), страница 47
Текст из файла (страница 47)
Поскольку здесь четыре аргумента, она не соответствует бинарной функции пах(), но соответствует второй функции с т = 1пс и 11вс = 1пс, 1пе. Следовательно, мьг выазуждены вызывать шаблон двоичной функции пах ( ) с первым аргументом, равным 1, и вторым аргументом, равным результату выполнения функции лшх (2, 3, 4) . Это опять не соответствует двоичной операции, и мы вьпываем версию с параметром списком,где Т = 1пс и 11вс = 1пс.
Наэтотраз подвыражениепвх(Ь,х) переходит в гвах ( 3, 4 ) и рекурсия заканчивается выбором бинарного шаблона Благодаря возможности перегружать шаблоны функций, этот метод работает довольно хорошо. Конечно, наше обсуждение на охватывает вопрос полностью.
Например, следовало бы точно определить, что в данном контексте означает параметр 11вп сопиев. Иногда желательно ссылаться на отдельные элементы или подмножества списка. Например, для этого можно было бы использовать индексные скобки. Следующий пример демонстрирует, как с помощью этой методики можно было бы построить метапрограмму для подсчета числа элементов в списке. Сепр1аее <Сурепагае Т> с1авв 11всркорв ( ри)э11с: еппзв ( 1епдс)з = 1 ); сегар1асе <...
11вг> с1авв Ь1всРкорв ( ри)э11с: епша ( 1епдг)г = 1+1 1вгркорв<11ве[1 ...)>:з1епдк)з ): Отсюда видно, что параметры-списки могут быть также полезными для шаблонов классов и их можно было бы соединить с рассмотренным ранее понятием перегрузки класса в целях усовершенствования различных методик метапрограммирования шаблонов. В качестве альтернативы, параметр-список можно использовать для объявления списка полей. еезар1аге <... 11вс> с1авв Со11ессйоп ( 11вс; ); удивительно, какое количество важных утилит можно создать на основе этого функционального средства.
Чтобы у вас появились новые идеи, предлагаем прочитать книгу Мог(егл С++ )зещл [Ц, где отсутствие такой функциональной возможности заменяется обширным мегапрограммированием на базе шаблонов и макроопределений. 250 Глава ]3. Направления дальнейшего развития 13.14. Управление размещением данных Трудная и довольно распространенная задача, связанная с программированием шаблонов, заключается в объявлении массива байтов, достаточно большого (но не более того), чтобы вмещать обьект пока не известного типа т; другими словами, являющегося параметром шаблона Одно из применений такого объявления — так называемые размеченные объединения (называемые также вариантныии типами или помеченными объединениями). Сежр1аСе <... 11вт> с1авв Р дпйоп ( рпЬ11с: епша ( и Ьусев ); сЬаг Ьугев[п Ьукев]; //в конечном счете будет // содержать элементы одного ив типов, // описанных аргументами шаблона Константе и Ьукев не всегда можно присваивать резулыат операции в1геоб (Т), так как для типа Т могут быть более строгие требования к выравниванию, чем для буфера Ьуг ее.
Существуют различные, полученные опытным путем правила, позволяющие учесть это выравнивание; но они зачастую сложны или имеют излишне вольные исходные посылки. Что здесь действительно желательно, так это возможность определять требования к выравниванию некоторого типа в виде константного выражения и, напротив, возможность выполнять выравнивание типа, поля или переменной в соответствии с этими требованиями.
Во многих компиляторах языков С и С++ уже есть операция а11дпоб которая возвращает требование к выравниванию выражения данного типа. Она почти идентична операции в 1 кео Г, за исключением того, что для данного типа вместо размера возвращает требование к его выравниванию. Для выполнения выравнивания какого- либо программного объекта во многих компиляторах уже имеются директивы ()ргадтаа или подобные им элементы. Возможным подходом является введение ключевою слова а11дпой, которое можно было бы использовать как в выражениях (для получения требования к выравниванию), так и в объявлениях (для выполнения выравнивания).
Сешр1аке <Сурепшае Т> с1авв й11дшаепс ( рпЬ11с: епша ( пах = а11дпоб(Т) ); сегар1асе <... 11вс> с1авв й11дглаепс ( рпЬ11с: епшп ( мах = а11дпоб( 11вт[0] > й11дшаепе<11ве[1 ...]>::гаах ? а11дпой(11вс[0]) 251 13.15. Вывод на основе иницнализатора А11дпшепс<11зс(1 ...]>::шахг // Для определения типа самого большого размера в данном // списке типов можно аналогичным образом создать набор // шаблонов Яйзе сешр1асе <... 11зс> с1азз т/агйапг ( риЫйс: сваг Ьиййег(яйяе<11зс>::шах] а11дпой (А11дпшепс< 11з с>:: пах г 13.15. Вывод на основе инициализатора Часто говорят, что "программисты ленивы", и иногда это относится к нашему желанию иметь компактные записи программ. Рассмотрим с этой точки зрения следующее объявление: вЫ::пар<згй::згг1пд, згс)::11зс<1пс»* Ысг = пем згб::пар<вас)::зсгйпд, зоб::11зг<1пс»; Оно многословно и на практике мы, вероятно (а скорее всего, наверняка), ввели бы для данного типа синоним с использованием конструкции суребей.
Однако в этом объявлении есть что-то лишнее: мы определяем тип 61сс, но он все равно неявно присутствует в типе инициалнзатора. Разве не элегантнее было бы записать эквивалентное объявление только с одним определением типа? Например: Йс1 61сс = пем зсб::шар<зсб::зггйпд, зсб::11зс<1пс»; В этом последнем объявлении тип переменной выводится из типа ее инициализатора. Чтобы это обьявление отличалось от обычной операции присвоения, необходимо ключевое слово (в данном случае с]с1, но предлагались также чаг, 1ес и даже апго). Этот вопрос касаепя не только шаблонов.
Оказывается, по в действительности такая конструкция была привпа в одной из самых первых версий компилятора Сйопг (в 1982 году, до того как появились шаблоны). Однако именно многословность объявлений многих типов в шаблонах делает потребность в этой функциональной возможности более настоятельной. Можно также представить частичный вывод, при котором логически должны выводиться только аргументы шаблона. вгб:: 11зг<> 1пс]ех = сгеасе 1пбех(); Другой вариант состоит в том, чтобы логически выводить аргументы шаблона из аргументов конструктора.
сешр1асе <еурепаше Т> с1авв Сошр1ех ( рпЫхо 3 252 Глава 13. Направления дальнейшего развития Сомр1ех(Т соплей ге, Т соплей 1м) Совр1ех<> х (1 . О, 3 . О) з //здесь выводится, что Т = йоиЬ1е Точные спецификации (определения) для этого вида вывода усложняются тем, что возможны перегруженные конструкторы и шаблоны конструкторов. Предположим, например, что наш шаблон Соир1ех содержит шаблон конструктора в дополнение к обычному конструктору копирования. еемр1асе «гурепаме т> с1авв Сомр1ех рцЬ11с: Сомр1ех(Сомр1ех<Т> соплей) Семр1аге <Сурепаме Т2> Сомр1ех(Сомр1ех<Т2> соплей) Сомр1ех<йоиЬ1е> 3(0.0, 1.0); Сотр1ех<> к = 3; //Какой конструктор имеется в виду? Вероятно, в последней инициализации имелся в виду стандартный конструктор копирования; следовательно, переменная к должна иметь тот же тип, что и 3.
Однако делать из этого неявное правило ипюрирования шаблонов конструкторов было бы чересчур смело. 13.16. Функциональные выражения В главе 22, "Обьекты-функции и обратные вызовы", проиллюстрирован тот факт, что часто бывает удобно передавать небольшие функции (или функторы) в другие функции в качестве параметров. В главе 17, "Метапрограммы", также упоминается, что методики создания шаблонов выражений можно использовать для построения небольших функторов в кратком виде, без явных объявлений (см. раздел 18.3, стр.
364). Например, может потребоваться вызывать особую функцию-член для каждого элемента стандартного вектора, чтобы его инициализировать. с1авв ВйсЧа1ие ( риЬ11с: чойй йпйс(); с1авв Хпйг ( рпЬ11с: чойй орегагог() (ВйоЧа1ией ч) сопле ( у.1пйг(); 13.16. Функциональные выражения 253 чоЫ созариге (вей:зчессог<ВедЧа1ие>й чес) ( вес)з юбог еас1з(чес.Ьедйп(), чес.епс)(), 1пйс() ); Необходимость определять для этого отдельный класс 1пйс ведет к громоздкому коду. Вместо этого можно представить себе возможность записывать (неименованные) тела функций как часть некоторого выражения. с1авв ВйдЧа1ие ( риЫйс з чоЫ йпйс() чоЫ соазрисе (всйз:чессог<В1дЧа1ие>й чес) ( все)ззйог еасЬ(чес.Ьедйп(), чес.епс)(), 9(Вз.дЧа1ией) ( $1.1пйг(); )); Идея заключается в том, чтобы ввести функз(канальное выражение со специальной лексемой В, за которой следуют типы параметров в круглых скобках и тело выражения в фигурных скобках.
Внутри такой конструкции к параметрам можно обращаться посредством специальной записи 9п, где и — это константа, указывающая номер параметра. Эта форма записи похожа на так называемые лямбда-выражения (или лямбдафункции) и замыкания из других языков программирования. Однако возможны и другие решения. Например, одним из решений могло бы быть использование анонимных внутренних классов, которые можно увидеть в языке )ача.
с1авв ВйдЧа1ие ( риЫйс: Ы 1пйс(): чоЫ созариге (вМ::чесгог<ВйдЧа1ие>й чес) ( всйз залог еасЬ(чес.Ьедйп(], чес.епс)(), с1авв ( риЫйс: чо1с) орегагог(] (В1дЧа1ией ч) сопле ч.йпйс()з Глава 13. Направления дальнейшего развития 254 Хотя конструкции такого рода регулярно появлжотся в среде разработчиков языка, конкретных предложений очень мало. Вероятно, это следствие того, что проектирование такого расширения — задача гораздо более сложная, чем можно предположить на основании наших примеров. Среди вопросов, которые должны быть решены, вопросы о спецификации возвращаемого типа и правилах, определяющих, какие программные элементы доступны в теле функционального выражения.
Например, разрешен ли доступ к локальным переменным в окружающих функциях? Можно было бы также представить, что функциональные выражения — это шаблоны, в которых типы параметров логически выводятся из конкретного применения функционального выражения. Такой подход может сделать предыдущий пример еще более кратким (позволяя совсем опусппь список параметров), но он ставит и новые задачи, касающиеся системы логического вывода аргументов шаблона. Совершенно неясно, будет ли когда-нибудь в язык С++ включено такое понятие, как функциональное выражение. Однако библиотека ЕаячЫа Ибгагу авторов Яакко Ярви ()яакко )дгч) и Гэри Пауэлла (Сагу Речей) [20) является большим шагом на пути обеспечения желаемых функциональных возможностей, хотя и за счет значительных ресурсов компилятора.
13.17. Заключение Сейчас, когда компиляторы языка С++ только становятся совместимыми в своей основе со стандартом 1998 года (С++98), пожалуй, преждевременно говорить о дальнейшем расширении языка Однако частично именно благодаря тому, что эта совместимость постепенно достигается, мы (сообщество программистов, пиш)лцих на С++) приобретаем способность ясно понимать истинные недостатки языка С++ (и шаблонов в частности). Идя навстречу новым потребностям программистов, пишущих на С++, Комитет по стандартам языка С++ (известный как 180 %021/АН31 116 или просто %02 1016) начал исследовать пути к новому стандарту: С++Ох.
После того как этот стандарт был предварительно представлен на заседании в Копенгагене в апреле 2001 года, комитет %02И16 начал рассматривать конкретные предложения по расширению библиотека В действительности намерение комитета состоит в том, чтобы попытаться, насколько это возможно, ограничить расширения стандартной библиотеки языка С++. Однако ясно, что некоторые из этих расширений потребуют изменений в самом ядре языка Мы ожидаем, что многие из этих необходимых модификаций будут иметь отношение к шаблонам языка С++, точно так же, как введение стандартной библиотеки шаблонов (8'П ) в стандартную библиотеку языка С++ стимулировало развитие шаблонов в 1990-х годах.