Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 15
Скотт Мейерс
Важно понимать, что const
(и volatile
) игнорируются только параметрами, передаваемыми по значению. Как мы уже видели, для параметров, которые являются ссылками или указателями на const
, константность expr
при выводе типа сохраняется. Но рассмотрим случай, когда expr
представляет собой const
-указатель на константный объект, а передача осуществляется по значению:
template<typename Т>
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
будет проигнорирована, а выведенным для 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<typename Т>
void f(T param); // Шаблон, получающий параметр по значению
f(name); // Какой тип Т и param будет выведен?
Начнем с наблюдения, что не существует такой вещи, как параметр функции, являющийся массивом. Да, да — приведенный далее синтаксис корректен:
void myFunc(int param[]);
Однако объявление массива рассматривается как объявление указателя, а это означает, что функция myFunc может быть эквивалентно объявлена как
void myFunc(int* param); // Та же функция, что и ранее