Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - страница 16
контейнера тип зависит от самого контейнера.
>decltype
упрощает выражение этой зависимости. Вот пример, показывающий применение >decltype
для вычисления возвращаемого типа. Этот шаблон требует уточнения, но пока что мы его отложим.
>template
>auto authAndAccess(Container& с, Index i) // требует
> ->decltype(c[i]) // уточнения
>{
> authenticateUser();
> return c[i];
>}
Использование >auto
перед именем функции не имеет ничего общего с выводом типа. На самом деле оно указывает, что использован синтаксис С++11 — завершающий возвращаемый тип (trailing return type), т.е. что возвращаемый тип функции будет объявлен после списка параметров (после “>->
”). Завершающий возвращаемый тип обладает тем преимуществом, что в спецификации возвращаемого типа могут использоваться параметры функции. В >authAndAccess
, например, мы указываем возвращаемый тип с использованием >с
и >i
. Если бы возвращаемый тип, как обычно, предшествовал имени функции, >c
и >i
были бы в нем недоступны, поскольку в этот момент они еще не были объявлены.
При таком объявлении >authAndAccess
возвращает тот тип, который возвращает >operator[]
при применении к переданному контейнеру, в точности как мы и хотели.
С++11 разрешает вывод возвращаемых типов лямбда-выражений из одной инструкции, а С++14 расширяет эту возможность на все лямбда-выражения и все функции, включая состоящие из множества инструкций. В случае >authAndAccess
это означает, что в С++ 14 мы можем опустить завершающий возвращаемый тип, оставляя только одно ведущее ключевое слово >auto
. При таком объявлении >auto
означает, что имеет место вывод типа. В частности, это означает, что компиляторы будут выводить возвращаемый тип функции из ее реализации:
>template
>auto authAndAccess(Container& с, Index i) // Не совсем
>{ // корректно
> authenticateUser();
> return c[i]; // возвращаемый тип выводится из c[i]
>}
В разделе 1.2 поясняется, что для функций с >auto
-спецификацией возвращаемого типа компиляторы применяют вывод типа шаблона. В данном случае это оказывается проблематичным. Как уже говорилось, >operator[]
для большинства контейнеров с объектами типа >Т
возвращает >Т&
, но в разделе 1.1 поясняется, что в процессе вывода типа шаблона “ссылочность” инициализирующего выражения игнорируется. Рассмотрим, что это означает для следующего клиентского кода:
>std::deque
>…
>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
следующим образом:
>template
>decltype(auto) // но все еще
>authAndAccess(Containers с, Index i) // требует
>{ // уточнения
> authenticateUser();
> return c[i];
>}
Теперь >authAndAccess
действительно возвращает то же, что и >c[i]
. В частности, в распространенном случае, когда