Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - [9]

Шрифт
Интервал

и >rx не могут быть модифицированы, ничего не говорит о том, может ли быть модифицирован >param. Вот почему константность >expr (как и >volatile, если таковой модификатор присутствует) игнорируется при выводе типа >param: то, что >expr не может быть модифицировано, не означает, что таковой должна быть и его копия.

Важно понимать, что >const>volatile) игнорируются только параметрами, передаваемыми по значению. Как мы уже видели, для параметров, которые являются ссылками или указателями на >const, константность >expr при выводе типа сохраняется. Но рассмотрим случай, когда >expr представляет собой >const-указатель на константный объект, а передача осуществляется по значению:

>template

>void f(Т param);        // param передается по значению


>const char* const ptr = // ptr - константный указатель на

> "Fun with pointers";   // константный объект


>f(ptr);                 // Передача arg типа const char* const

Здесь >const справа от звездочки объявляет >ptr константным: >ptr не может ни указывать на другое место в памяти, ни быть обнуленным. (>const слева от звездочки гласит, что >ptr указывает на то, что (строка символов) является const, а следовательно, не может быть изменено.) Когда >ptr передается в функцию >f, биты, составляющие указатель, копируются в >param. Как таковой сам указатель (ptr) будет передан по значению. В соответствии с правилом вывода типа при передаче параметров по значению константность >ptr будет проигнорирована, а выведенным для >param типом будет >const char*, т.е. изменяемый указатель на константную строку символов. Константность того, на что указывает >ptr, в процессе вывода типа сохраняется, но константность самого >ptr игнорируется при создании нового указателя >param.

Аргументы-массивы

Мы рассмотрели большую часть материала, посвященного выводу типов шаблонов, но есть еще один угол, в который стоит заглянуть. Это отличие типов массивов от типов указателей, несмотря на то что зачастую они выглядят взаимозаменяемыми. Основной вклад в эту иллюзию вносит то, что во множестве контекстов массив преобразуется в указатель на его первый элемент. Это преобразование позволяет компилироваться коду наподобие следующего:

>const char name[] = "Briggs";  // Тип name - const char[13]

>const char * ptrToName = name; // Массив становится указателем

Здесь указатель >ptrToName типа >const char* инициализируется переменной name, которая имеет тип >const char[13].Эти типы (>const char* и >const char[13]) не являются одним и тем же типом, но благодаря правилу преобразования массива в указатель приведенный выше код компилируется.

Но что будет, если передать массив шаблону, принимающему параметр по значению?

>template

>void f(T param); // Шаблон, получающий параметр по значению


>f(name);         // Какой тип Т и param будет выведен?

Начнем с наблюдения, что не существует такой вещи, как параметр функции, являющийся массивом. Да, да — приведенный далее синтаксис корректен:

>void myFunc(int param[]);

Однако объявление массива рассматривается как объявление указателя, а это означает, что функция myFunc может быть эквивалентно объявлена как

>void myFunc(int* param); // Та же функция, что и ранее

Эта эквивалентность параметров, представляющих собой массив и указатель, образно говоря, представляет собой немного листвы от корней С на дереве С++ и способствует возникновению иллюзии, что типы массивов и указателей представляют собой одно и то же.

Поскольку объявление параметра-массива рассматривается так, как если бы это было объявление параметра-указателя, тип массива, передаваемого в шаблонную функцию по значению, выводится как тип указателя. Это означает, что в вызове шаблонной функции >f ее параметр типа выводится как >const char*:

>f(name); // nаmе - массив, но Т - const char*

А вот теперь начинаются настоящие хитрости. Хотя функции не могут объявлять параметры как истинные массивы, они могут объявлять параметры, являющиеся ссылками на массивы! Так что если мы изменим шаблон >f так, чтобы он получал свой аргумент по ссылке,

>template

>void f(T& param); // Шаблон с передачей параметра по ссылке

и передадим ему массив

>f(name); // Передача массива функции f

то тип, выведенный для , будет в действительности типом массива! Этот тип включает размер массива, так что в нашем примере выводится как >const char[13], а типом параметра >f (ссылки на этот массив) является >const char (&)[13]. Да, выглядит этот синтаксис как наркотический бред, но знание его прибавит вам веса в глазах понимающих людей.

Интересно, что возможность объявлять ссылки на массивы позволяет создать шаблон, который выводит количество элементов, содержащихся в массиве:

>// Возвращает размер массива как константу времени компиляции.

>// Параметр не имеет имени, поскольку, кроме количества

>// содержащихся в нем элементов, нас ничто не интересует.

>template

>constexpr std::size_t arraySize(T (&)[N]) nоехсерt {

> return N;

>}

Как поясняется в разделе 3.9, объявление этой функции как >constexpr делает ее результат доступным во время компиляции. Это позволяет объявить, например, массив с таким же количеством элементов, как и у второго массива, размер которого вычисляется из инициализатора в фигурных скобках:


Еще от автора Скотт Мейерс
Эффективное использование STL

В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.


Как функции, не являющиеся методами, улучшают инкапсуляцию

Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.


Рекомендуем почитать
Изучаем Java EE 7

Java Enterprise Edition (Java EE) остается одной из ведущих технологий и платформ на основе Java. Данная книга представляет собой логичное пошаговое руководство, в котором подробно описаны многие спецификации и эталонные реализации Java EE 7. Работа с ними продемонстрирована на практических примерах. В этом фундаментальном издании также используется новейшая версия инструмента GlassFish, предназначенного для развертывания и администрирования примеров кода. Книга написана ведущим специалистом по обработке запросов на спецификацию Java EE, членом наблюдательного совета организации Java Community Process (JCP)


Виртуальная библиотека Delphi

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка баз данных на Visual Basic.NET

Это практическое руководство разработчика программного обеспечения на Visual Basic .NET и ADO.NET, предназначенное для создания приложений баз данных на основе WinForms, Web-форм и Web-служб. В книге описываются практические способы решения задач доступа к данным, с которыми сталкиваются разработчики на Visual Basic .NET в своей повседневной деятельности. Книга начинается с основных сведений о создании баз данных, использовании языка структурированных запросов SQL и системы управления базами данных Microsoft SQL Server 2000.


Исчерпывающее руководство по написанию всплывающих подсказок

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Программное обеспечение встроенных систем. Общие требования к разработке и документированию

Embedded system software. General requirements for development and documentationСтандарт подготовлен в развитие ГОСТ Р ИСО/МЭК 12207-99 «Информационная технология. Процессы жизненного цикла программных средств» с целью учета специфики разработки и документирования программного обеспечения встроенных систем реального времени.


Как пасти котов. Наставление для программистов, руководящих другими программистами

«Как пасти котов» – это книга о лидерстве и руководстве, о том, как первое совмещать со вторым. Это, если хотите, словарь трудных случаев управления IT-проектами. Программист подобен кошке, которая гуляет сама по себе. Так уж исторически сложилось. Именно поэтому так непросто быть руководителем команды разработчиков. Даже если вы еще месяц назад были блестящим и дисциплинированным программистом и вдруг оказались в роли менеджера, вряд ли вы знаете, с чего надо начать, какой выбрать стиль руководства, как нанимать и увольнять сотрудников, проводить совещания, добиваться своевременного выполнения задач.