С. Мейерс - Эффективный и современный C++ (1114942), страница 5
Текст из файла (страница 5)
Каждый из них основанна нашем общем виде шаблонов и их вызова:ternplate<typename Т>void f(Param7YPE1 pararn) ;f (expr);24/ / Вывод Т иГлава 1. Вывод типовParamTypeизexprСлучай 1. Param7Y,pe я вляется указателе м или ссы лкой,но не уни версальной ссылкойПростейшая ситуация - когда Pa ramType является ссылочным типом или типом указателя, но не универсальной ссылкой.
В этом случае вывод типа работает следующимобразом.1 . Если типом expr является ссылка, ссылочная часть игнорируется.2. Затем выполняется сопоставление типа expr с ParamType для определеният.Например, если у нас имеются шаблонtemplate<typename Т>void f (T& param) ;// param представляет собой ссыпкуи объявления переменных11 х имеет тип intint х = 27 ;// с х имеет тип const intconst int сх = х ;const int& rx = х; // rx является ссыпкой на х как на const intто выводимые типы для pa ram и Т в различных выводах будут следующими:f ( х ) ; / / Т - int ,тип param - int&f ( cx) ; / / Тconst int , тип param - coпst int&f ( rx ) ; / / Т - const int , тип param - const int&-Во втором и третьем вызовах обратите внимание, что, поскольку сх и rx объявленыкак константные значения, Т выводится как const int , тем самым приводя к типу параметра const int &.
Это важно для вызывающего кода. Передавая константный объектпараметру-ссылке, он ожидает, что объект останется неизменным, т.е. что параметр будетпредставлять собой ссылку на const . Вот почему передача константного объекта в шаблон,получающий параметр Т&, безопасна: константность объекта становится частью выведенного для Т типа.В третьем примере обратите внимание, что несмотря на то, что типом rx являетсяссылка, тип т выводится как не ссылочный. Вот почему при выводе типа игнорируется"ссылочность" rx.Все эти примеры показывают ссылочные параметры, являющиеся lvalue, но вывод типа точно так же работает и для ссылочных параметров rvalue.
Конечно, rvаluеаргументы могут передаваться только ссылочным параметрам, являющимся rvalue, ноэто ограничение никак не влияет на вывод типов.Если мы изменим тип параметра f с Т & на const Т &, произойдут небольшие изменения, но ничего удивительного не случится. Константность сх и rx продолжает соблюдаться, но поскольку теперь мы считаем, что pa ram является ссылкой на const , const какчасть выводимого типа т не требуется:template<typename Т>void f (const Т& param) ; / / param является ссыпкой на const1.1.Вывод типа wабnона2527;int хconst int СХconst int& rx1 1 К а к и ранее1 1 Как и ранее11 Как и ранее==х;= х;1 1 т - int , тип param - const int&11 тint , тип param - const int&11 тint , тип pa ram - const int &f(х) ;f (сх) ;f (rx) ;Как и ранее, "ссылочность" rx при выводе типа игнорируется.Если бы param был указателем (или указателем на const), а не ссылкой, все бы работало, по сути, точно так же:template<typename Т>// Теперь param является указателемvoid f (T* param ) ;int х = 27 ;const int *рх11 Как и ранее& х ; // рх - указатель на х, как на const int11 Т - iпt ,тип param - int*// Т - const int, тип param - const int*f( &х ) ;f (рх ) ;Сейчас вы можете обнаружить, что давно усердно зеваете, потому что все это оченьскучно, правила вывода типов в С++ работают так естественно для ссылок и указателей,что все просто очевидно! Это именно то, что вы хотите от системы вывода типов.Случай 2.
Param�e я вляется уни версальной ссылкойВсе становится менее очевидным в случае шаблонов, принимающих параметры, являющиеся универсальными ссылками. Такие параметры объявляются как ссылки rvalue(т.е. в шаблоне функции, принимающем параметр типа т, объявленным типом универсальной ссылки является Т&&), но ведут себя иначе при передаче арrументов, являющихся lvalue. Полностью вопрос рассматривается в разделе 5.2, здесь приводится его сокращенная версия.•Если expr представляет собой lvalue, как Т, так и ParamType выводятся как \vа\uессылки. Это вдвойне необычно.
Во-первых, это единственная ситуация в выводетипа шаблона, когда Т выводится как ссылка. Во-вторых, хотя ParamType объявлен с использованием синтаксиса rvаluе-ссылки, его выводимым типом является\vаluе-ссылка.•Если expr представляет собой rvalue, применяются "обычные" правила (из случая l).Примерыtemplate<typename Т>void f ( T&& param) ; // param является универсальной ссылкойint х = 27 ;const int сх26х;11 Как и ранее1 1 Как и ранееГnава 1. Вывод типовconst int& rxх; 11 Как и ранееf (х) ;f ( сх ) ;f ( rx) ;f(27) ;1111//1111111111х - lvalue, так что Т - iпt & ,тип pararn также является iпt&с х - lvalue , так что Т - const iпt & ,тип pararn также является coпst iпt&rx - lvalue , так что Т - const iпt& ,тип pararn также является const iпt&2 7 - rvalue , так что Т - int ,следовательно, тип param - iпt & &В разделе 5.2 поясняется, почему эти примеры работают именно так, а не иначе.
Ключевым моментом является то, что правила вывода типов для параметров, являющихсяуниверсальными ссылками, отличаются от таковых для параметров, являющихся lvalueили rvаluе-ссылками. В частности, когда используются универсальные ссылки, выводтипов различает аргументы, являющиеся lvalue, и аргументы, являющиеся rvalue.
Этогоникогда не происходит для неуниверсальных ссылок.Случай 3. Param�e не я вляется ни указателем, ни ссылкойКогда Pa rarnType не является ни указателем, ни ссылкой, мы имеем дело с передачейпо значению:ternplate<typeпarne Т>void f (T pararn) ;// param передается по значениюЭто означает, что pararn будет копией переданного функции - совершенно новымобъектом. Тот факт, что pararn будет совершенно новым объектом, приводит к правилам,которые регулируют вывод Т из expr.l.Как и ранее, если типом expr является ссылка, ссылочная часть игнорируется.2.
Если после отбрасывания ссылочной части expr является const, это также игнорируется. Игнорируется и модификатор volat i l e (объекты vo l at i le являются редкостью и в общем случае используются только при реализации драйверовустройств; детальную информацию на эту тему вы найдете в разделе 7.6.)Таким образом, получаем следующее:int х = 2 7 ;const int сх = х ;const int& rx = х;f (х) ;f ( сх ) ;f ( rx ) ;111111111111Как и ранееКак и ранееКак и ранееТипами и Т , и pararn являются intТипами и Т , и pararn внов ь являются iпtТипами и Т , и pararn опять являются iпtОбратите внимание, что даже несмотря на то, что сх и rx представляют константныезначения, pa rarn не является coпst. Это имеет смысл.
pararn представляет собой объект,который полностью независим от сх и rx,это копия сх или rx. Тот факт, что сх и rx-1 . 1 . Вывод типа шаблона27не могут быть модифицированы, ничего не говорит о том, может ли быть модифицирован pararn. Вот почему константность expr (как и volat i le, если таковой модификаторприсутствует) игнорируется при выводе типа param: то, что expr не может быть модифицировано, не означает, что таковой должна быть и его копия.Важно понимать, что const (и volat i le) игнорируются только параметрами, передаваемыми по значению. Как мы уже видели, для параметров, которые являются ссылкамиили указателями на const, константность expr при выводе типа сохраняется.
Но рассмотрим случай, когда expr представляет собой соnst -указатель на константный объект,а передача осуществляется по значению:template<typename Т>void f (Т param) ;11 param передается по значению11 ptr - константный указатель наconst char* const ptr" Fun with pointers" ; // константный объект=f (ptr) ;11 Передача arg типа const char* constЗдесь const справа от звездочки объявляет ptr константным: pt r не может ни указыватьна другое место в памяти, ни быть обнуленным.
(const слева от звездочки гласит, чтоptr указывает на то, что (строка символов) является const, а следовательно, не можетбыть изменено.) Когда pt r передается в функцию f, биты, составляющие указатель, копируются в param. Как таковой сам указатель (ptr) будет передан по значению. В соответствии с правилом вывода типа при передаче параметров по значению константность pt rбудет проигнорирована, а выведенным для param типом будет const char*, т.е. изменяемый указатель на константную строку символов. Константность того, на что указываетptr, в процессе вывода типа сохраняется, но константность самого ptr игнорируется присоздании нового указателя param.Ар гументы - масси выМы рассмотрели большую часть материала, посвященного выводу типов шаблонов,но есть еще один угол, в который стоит заглянуть.
Это отличие типов массивов от типов указателей, несмотря на то что зачастую они выглядят взаимозаменяемыми. Основной вклад в эту иллюзию вносит то, что во множестве контекстов массив преобразуетсяв указатель на его первый элемент. Это преобразование позволяет компилироваться кодунаподобие следующего:const char name [ ] = "Briggs" ;const char * ptrToName = name ;// Тип name - const char [ l З J/ / Массив становится указателемЗдесь указатель pt rToName типа const char* инициализируется переменной name, которая имеет тип const char ( 1 3 ] . Эти типы (const cha r* и const char [ 1 3 ] ) не являютсяодним и тем же типом, но благодаря правилу преобразования массива в указатель приведенный выше код компилируется.Но что будет, если передать массив шаблону, принимающему параметр по значению?28Глава 1 .