Проблема в том, что, глядя на тип параметра функции func(), невозможно определить уникальный тип для аргумента шаблона. Вызов функции func() мог бы создать экземпляр версии функции compare(), получающей целые числа или версию, получающую строки. Поскольку невозможно идентифицировать уникальный экземпляр для аргумента функции func(), этот вызов не будет откомпилирован.

Неоднозначность вызова функции func() можно устранить при помощи явных аргументов шаблона:

// ok: явно определенная версия экземпляра compare()

func(compare); // передача compare(const int&, const int&)

Это выражение вызывает версию функции func(), получающую указатель на функцию с двумя параметрами типа const int&.

Когда возвращается адрес экземпляра шаблона функции, контекст должен позволять однозначно идентифицировать тип или значение для каждого параметра шаблона.

<p><image l:href="#books.png"/>16.2.5. Дедукция аргумента шаблона и ссылки</p>

Чтобы лучше понять дедукцию типа, рассмотрим такой вызов функции где параметр функции p является ссылкой на параметр типа шаблона T:

template void f(Т &p);

Обратите внимание на два момента: здесь применяются обычные правила привязки ссылок; и спецификаторы const здесь нижнего уровня, а не верхнего.

Дедукция типа из параметров ссылки на l-значения функций

Когда параметр функции представляет собой обычную ссылку (l-значение) на параметр типа шаблона (т.е. имеющего форму T&), правила привязки гласят, что передавать можно только l-значения (например, переменная или выражение, возвращающее ссылочный тип). Этот аргумент может быть или не быть константным. Если аргумент будет константой, то тип Т будет выведен как константный:

template void f1(Т&); // аргумент должен быть l-значением

// вызовы f1() используют ссылочный тип аргумента как тип параметра

// шаблона

f1(i);  // i - это int; параметр шаблона Т - это int

f1(ci); // ci - это const int; параметр шаблона Т - это const int

f1(5);  // ошибка: аргумент ссылочного параметра

        // должен быть l-значением

Если параметр функции имеет тип const Т&, обычные правила привязки гласят, что можно передать любой вид аргумента — объект (константный или нет), временный объект или литеральное значение. Когда сам параметр функции является константой, выведенный для параметра Т тип не будет константным типом. Константность является частью типа параметра функции, и поэтому она не становится также частью типа параметра шаблона:

template void f2(const T&); // может получать r-значения

// параметр в f2() - это const &; const в аргументе неуместен

// в каждом из этих трех вызовов параметр функции f2() выводится

// как const int&

f2(i);  // i - это int; параметр шаблона Т - это int

f2(ci); // ci - это const int, но параметр шаблона T - это int

f2(5);  // параметр const & может быть привязан к r-значению;

        // Т - это int

Дедукция типа из параметров ссылки на r-значения функций

Когда параметр функции является ссылкой на r-значение (см. раздел 13.6.1), т.е. имеет форму Т&&, обычные правила привязки гласят, что этому параметру можно передать r-значение. При этом дедукция типа ведет себя таким же образом, как дедукция обычного ссылочного параметра функции на l-значение. Выведенный тип для параметра Т — это тип r-значения:

template void f3(T&&);

f3(42); // аргумент - это r-значение типа int; параметр

        // шаблона Т - это int

Сворачивание ссылок и параметры ссылок на r-значения

Предположим, что i является объектом типа int. Можно подумать, что такой вызов, как f3(i), будет недопустим. В конце концов, i — это l-значение, а ссылку на r-значение обычно нельзя связать с l-значением. Однако язык определяет два исключения из обычных правил привязки, которые позволяют это. На этих исключениях из правил основан принцип работы таких библиотечных функций, как move().

Перейти на страницу:

Похожие книги