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

Скотт Мейерс

Важно понимать, что constvolatile) игнорируются только параметрами, передаваемыми по значению. Как мы уже видели, для параметров, которые являются ссылками или указателями на 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) будет передан по значению. В соответствии с правилом вывода типа при передаче параметров по значению константность 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); // Та же функция, что и ранее