Читать «Эффективный и современный С++. 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 следующим образом: