Эффективный и современный С++. 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