Читать «Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14» онлайн - страница 22
Скотт Мейерс
При таком объявлении authAndAccess
возвращает тот тип, который возвращает operator[]
при применении к переданному контейнеру, в точности как мы и хотели.
С++11 разрешает вывод возвращаемых типов лямбда-выражений из одной инструкции, а С++14 расширяет эту возможность на все лямбда-выражения и все функции, включая состоящие из множества инструкций. В случае authAndAccess
это означает, что в С++ 14 мы можем опустить завершающий возвращаемый тип, оставляя только одно ведущее ключевое слово auto
. При таком объявлении auto
означает, что имеет место вывод типа. В частности, это означает, что компиляторы будут выводить возвращаемый тип функции из ее реализации:
template<typename Container, typename Index> // С++14;
auto authAndAccess(Container& с, Index i) // Не совсем
{ // корректно
authenticateUser();
return c[i]; // возвращаемый тип выводится из c[i]
}
В разделе 1.2 поясняется, что для функций с auto
-спецификацией возвращаемого типа компиляторы применяют вывод типа шаблона. В данном случае это оказывается проблематичным. Как уже говорилось, operator[]
для большинства контейнеров с объектами типа Т
возвращает Т&
, но в разделе 1.1 поясняется, что в процессе вывода типа шаблона “ссылочность” инициализирующего выражения игнорируется. Рассмотрим, что это означает для следующего клиентского кода:
std::deque<int> d;
…
authAndAccess(d, 5) = 10; // Аутентифицирует пользователя, воз-
// вращает d[5], затем присваивает ему
// значение 10. Код не компилируется!
Здесь d[5]
возвращает int&
, но вывод возвращаемого типа auto
для authAndAccess
отбрасывает ссылку, тем самым давая возвращаемый тип int
. Этот int
, будучи возвращаемым значением функции, является rvalue
, так что приведенный выше код пытается присвоить этому rvalue
типа int значение 10. Это запрещено в С++, так что данный код не компилируется.
Чтобы заставить authAndAccess
работать так, как мы хотим, нам надо использовать для ее возвращаемого типа вывод типа decltype
, т.е. указать, что authAndAccess
должна возвращать в точности тот же тип, что и выражение с[i]
. Защитники С++, предвидя необходимость использования в некоторых случаях правил вывода типа decltype
, сделали это возможным в С++14 с помощью спецификатора decltype(auto)
. То, что изначально может показаться противоречием (decltype
auto
?), в действительности имеет смысл: auto
указывает, что тип должен быть выведен, а decltype
говорит о том, что в процессе вывода следует использовать правила decltype
. Итак, можно записать authAndAccess
следующим образом: