С. Мейерс - Эффективный и современный C++ (1114942), страница 17
Текст из файла (страница 17)
Если вы применяете их к параметру типа в шаблоне (что практически всегдаявляется их применением в реальном коде), то вы также должны предварять каждое ихприменение ключевым словом t ypename. Причина обоих этих синтаксических требований заключается в том, что свойства типов в C++l l реализованы как вложенные t ypedef"76Гn ава 3. Переход к современному С++внутри шаблонных структур struct. Да, это так - они реализованы с помощью технологии, о которой я говорю, что она уступает шаблонам псевдонимов!Тому есть исторические причины, но здесь мы их опустим (честное слово, это слишком скучно), поскольку Комитет по стандартизации с опозданием признал, что шаблоныпсевдонимов оказываются лучшим способом реализации, и соответствующие шаблонывключены в С++ \4 для всех преобразований типов C++ l l . Псевдонимы имеют общийвид: для каждого преобразования C++ l l std : : преобразование<Т> : : t ype имеется соответствующий шаблон псевдонима С++ 14 с именем std : : преобразов ание_ t.
Вот примеры, поясняющие, что я имею в виду:std:std :std :std :std :std :: remove_const<T> : : type: remove const t<T>: remove_reference<T> : : type: remove reference t<T>: add_lva lue_reference<T> : : type: add lvalue reference t<T>11 C++ l l : const Т -> Т/ / Эквивалент в С++ 1 4/ / C++l l : Т & / Т & & - > Т11 Эквивалент в С++ 1 4/ / C++l l : Т - > Т&/ / Эквивалент в С++ 1 4Конструкции С++ 1 1 остаются в силе в С++ 1 4, но я не знаю, зачем вам может захотеться их использовать. Даже если у вас нет компилятора С++ 14, написание таких шаблоновпсевдонимов самостоятельно - детская игра.
Требуются только языковые возможностиС++ 1 1, и даже ребенок сможет написать такие шаблоны. Если у вас есть доступ к электронной копии стандарта С++ 14, то все становится еще проще - вы можете просто скопировать необходимый код оттуда и вставить в свою программу. Вот вам для начала:template <class Т>using remove_const ttypename remove_const<T> : : type ;template <class Т>using remove_reference ttypename remove_reference<T> : : type ;template <class Т>using add_lvalue_re ference t =typename add_lvalue_reference<T> : : type ;Судите сами - что может быть проще?Следует запомнить••В отличие от объявлений псевдонимов, typede f не поддерживает шаблонизацию.Шаблоны псевдонимов не требуют суффикса : : t уре ·: а в шаблонах - префикса"t ypename, часто требуемого при обращении к t ypede f.•С++ 14 предлагает шаблоны псевдонимов для всех преобразований свойств типовC++ l l .3.3.
Предпочитайте объявление псевдонимов применению typedef773 .4. П редпочита йте перечисл ения с областьювидимости перечислениям без таковойВ качестве общего правила объявление имени в фигурных скобках ограничиваетвидимость этого имени областью видимости, определяемой этими скобками. Но не такобстоит дело с перечислениями в С++98. Имена в таких перечислениях принадлежат области видимости, содержащей епшn, а это означает, что ничто иное в этой области видимости не должно иметь такое же имя:enum Color {Ьlack, white, red} ; / / Ыасk, white , red находятся// в той же области видимости,11 что и Color// Ошибка ! Имя white уже объявauto whitefalse ;// лено в этой области видимостиТот факт, что эти имена перечисления "вытекаютn в область видимости, содержащуюопределение их enшn, приводит к официальному термину для данной разновидности перечислений: без о бласти в идимости ( unscoped) .
Их новый аналог в С++ 1 1 , перечисленияс о бластью ви д имости ( scope d enum ) , не допускает такой утечки имен:enUlll class Color{ Ыасk, white, red } ; // Ыасk, white, red принадлежат11 области видимости Colorauto white = false ;// ОК, других white нетColor с=white;// Ошибка ' Нет имени перечисления// "white" в этой области видимостиColor с=Color : : white;11 окauto с = Color : : white;1111ОК (и соответствует советуиз раздела 2 . 1 )Поскольку eпum с областью видимости объявляются с помощью ключевого словаclass, о них иногда говорят как о классах перечислений.Снижение загрязнения пространства имен, обеспечиваемое применением перечислений с областью видимости, само по себе является достаточной причиной для предпочтения таких перечислений их аналогам без областей видимости.
Однако перечисленияс областью видимости имеют и второе убедительное преимущество: они существенностроже типизированы. Значения в перечислениях без областей видимости неявно преобразуются в целочисленные типы (а оттуда - в типы с плавающей точкой). Поэтомувполне законными оказываются такие семантические карикатуры:1 1 Перечисление без1 1 области видимостиstd: : vector<std : : s i ze t>1 1 Функция, возвращающаяprimeFactors ( s td : : size t х ) ; 1 1 простые делители хeпum Color { Ьlack, white, red } ;78Гnава 3.
Переход к современному С++Color с=red;11 Сравнение Color и douЫe ( ! )11 Вычисление простых делителей11 значения Color ( ! )if (с < 1 4 . 5 ) {auto factorsprimeFactors ( c ) ;Добавление простого ключевого слова c l a s s после enum преобразует перечислениебез области видимости в перечисление с областью видимости, и это - совсем другаяистория. Не имеется никаких неявных преобразований элементов перечисления с областью видимости в любой другой тип:/ / Перечисление с областью видимостиeпum class Color[ Ьlack, white, red } ;111111if (с < 1 4 .
5 ) {1111auto factorsprimeFactors ( c ) ; 11Color с=Color : : red;К а к и ранее, но с квалификаторомобласти ВИДИМОСТИОшибка ! Нельзя сравниватьColor и douЫeОшибка ! Нельзя передавать Color вфункцию, ожидающую std: : size tЕсли вы хотите честно выполнить преобразование из Color в другой тип, сделайте тоже, что вы всегда делаете для осуществления своих грязных желаний, - воспользуйтесьявным приведением типа:if (static_cast<douЫe> (c) < 1 4 . 5 ) { / / Странный, но11 корректный код11 Сомнительно, но компилируетсяauto factorsprimeFactors ( static_cast<std: : size_t> ( c) ) ;=Может показаться, что перечисления с областями видимости имеют и третье преимущество перед перечислениями без областей видимости, поскольку могут быть предварительно объявлены, их имена могут быть объявлены без указания перечислителей:eпum Color;eпum class Color;11 Ошибка !11 окЭто заблуждение.
В С++ 1 1 перечисления без областей видимости также могут бытьобъявлены предварительно, но только с помощью небольшой дополнительной работы,которая вытекает из того факта, что каждое перечисление в С++ имеет целочисленныйбазовый тип (underlying type), который определяется компилятором.
Для перечислениябез области видимости наподобие Coloreпum Color { Ы асk, white, red } ;3.4. Предпочитайте перечисления с область ю видимости перечис лениям без таковой79компилятор может выбрать в качестве базового типа char, так как он должен представить всего лишь три значения. Однако некоторые перечисления имеют куда большийдиапазон значений, например:enum Status { goodО,failed = 1 ,incomplete = 1 0 0 ,corrupt200,indeterminate = OxFFFFFFFF};==Здесь должны быть представлены значения в диапазоне от О до OxFFFFFFFF.
За исключением необычных машин (где char состоит как минимум из 32 битов), компилятор выберет для предоставления значений Status целочисленный тип, больший, чем cha r.Для эффективного использования памяти компиляторы часто выбирают наименьшийбазовый тип, которого достаточно для представления значений перечислителей. В некоторых случаях, когда компиляторы выполняют оптимизацию по скорости, а не по размеру, они могут выбрать не наименьший допустимый тип, но при этом они, определенно,захотят иметь возможность оптимизации размера. Для этого С++98 поддерживает только определения e num (в которых перечислены все значения); объявления e num не разрешены.
Это позволяет компиляторам выбирать базовый тип для каждого enum до егоиспользования.Но невозможность предварительного объявления e num имеет свои недостатки. Наиболее важным из них, вероятно, является увеличение зависимостей при компиляции.Вновь обратимся к перечислению St atus:enum Status { good = О ,failed = 1 ,incomplete100,corrupt = 2 0 0 ,indeterminate = OxFFFFFFFF};=Это разновидность перечисления, которая, скорее всего, будет использоваться во всейсистеме, а следовательно, включенная в заголовочный файл, от которой зависит каждаяиз частей системы. Если добавить новое значения состоянияenum Status { goodО,fai led1,incomplete100,corrupt200,audi ted = 500 ,indeterminateOxFFFFFFFF};=====то, вероятно, придется перекомпилировать всю систему полностью, даже если толькоодна подсистема - возможно, одна-единственная функция! - использует это новое80Гла ва З .
Переход к современному С++значение. Это одна из тех вещей, которые программисты просто ненавидят. И это тавещь, которую исключает возможность предварительного объявления eпum в С++ 1 1 . Например, вот совершенно корректное объявление eпum с областью видимости, и функции,которая получает его в качестве параметра:/ / Предварительное объявлениеепшп class Status;void coпtiпueProcess iпg ( Status s ) ; / / и его использованиеЗаголовочный файл, содержащий эти объявления, не требует перекомпиляции при пересмотре определения Status. Кроме того, если изменено перечисление Status (например,добавлено значение audited) , но поведение coпt iпueProcessiпg не изменилось (например, потому что coпt iпue Proces s i пg не использует значение audited) , то не требуетсяи перекомпиляция реализации coпt iпueProcessiпg.Но если компилятор должен знать размер eпum до использования, то как могут перечисления С++ 1 1 быть предварительно объявлены, в то время как перечисления С++98этого не могут� Ответ прост: базовый тип перечислений с областью видимости всегдаизвестен, а для перечислений без областей видимости вы можете его указать.По умолчанию базовым типом для eпum с областью видимости является iпt:епшп class Status ; / / Базовый тип - iпtЕсли вас не устраивает значение по умолчанию, вы можете его перекрыть:епшп class Status : std : : uint32_t; / / Базовый тип для Status 11 std : : uint32 t (из <cstdint>)В любом случае компиляторы знают размер перечислителей в перечислении с областьювидимости.Чтобы указать базовый тип для перечисления без области видимости, вы делаете тоже, что и для перечисления с областью видимости, и полученный результат может бытьпредварительно объявлен:епшп Color : std: : uintB_t ; // Предварительное объявление11 перечисления без области видимости ;11 базовый тип - std : : uint8 tСпецификация базового типа может быть указана и в определении eпum:еnшп class Status : std: : uint32 tgoodО,failed = 1 ,incomplete = 1 00 ,corrupt200,audited500,indeterminateOxFFFFFFFF====};С учетом того факта, что eпum с областью видимости устраняет загрязнение пространства имен и невосприимчиво к бессмысленным преобразованиям типов, вас может удивить тот факт, что имеется как минимум одна ситуация, в которой могут быть полезны3 .4.