Эффективный и современный С++. 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 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.


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

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


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

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


Симуляция частичной специализации

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


Обработка событий в С++

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


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.