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

стр.

и >rx, — это копия>cx или >rx. Тот факт, что >cx и >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