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

Шрифт
Интервал

для вычисления возвращаемого типа. Этот шаблон требует уточнения, но пока что мы его отложим.

>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 // С++14;

>auto authAndAccess(Container& с, Index i)    // Не совсем

>{                                            // корректно

> authenticateUser();

> return c[i];       // возвращаемый тип выводится из c[i]

>}

В разделе 1.2 поясняется, что для функций с >auto-спецификацией возвращаемого типа компиляторы применяют вывод типа шаблона. В данном случае это оказывается проблематичным. Как уже говорилось, >operator[] для большинства контейнеров с объектами типа возвращает >Т&, но в разделе 1.1 поясняется, что в процессе вывода типа шаблона “ссылочность” инициализирующего выражения игнорируется. Рассмотрим, что это означает для следующего клиентского кода:

>std::deque 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 следующим образом:

>template // С++14; работает,

>decltype(auto)                               // но все еще

>authAndAccess(Containers с, Index i)         // требует

>{                                            // уточнения

> authenticateUser();

> return c[i];

>}

Теперь >authAndAccess действительно возвращает то же, что и >c[i]. В частности, в распространенном случае, когда >c[i] возвращает >Т&, >authAndAccess также возвращает >Т&, и в том редком случае, когда >c[i] возвращает объект, >authAndAccess также возвращает объект.

Использование >decltype(auto) не ограничивается возвращаемыми типами функций. Это также может быть удобно для объявления переменных, когда вы хотите применять правила вывода типа >decltype к инициализирующему выражению:

>Widget w;

>const Widget& cw = w;


>auto myWidget1 = cw;           // Вывод типа auto:


>                               // тип myWidget1 - Widget

>decltype(auto) myWidget2 = cw; // Вывод типа decltype:

>                               // тип myWidget2 - const Widget&

Я знаю, что вас беспокоят два момента. Один из них — упомянутое выше, но пока не описанное уточнение >authAndAccess. Давайте, наконец-то, разберемся в этом вопросе. Еще раз посмотрим на версию >authAndAccess в С++14:

>template

>decltype(auto) authAndAccess(Container& с, Index i);

Контейнер передается как lvalue-ссылка на неконстантный объект, поскольку возвращаемая ссылка на элемент контейнера позволяет клиенту модифицировать этот контейнер. Но это означает, что этой функции невозможно передавать контейнеры, являющиеся rvalue. rvalue невозможно связать с lvalue-ссылками (если только они не являются lvalue-ссылками на константные объекты, что в данном случае очевидным образом не выполняется).


Еще от автора Скотт Мейерс
Эффективное использование STL

В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.


Как функции, не являющиеся методами, улучшают инкапсуляцию

Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.


Рекомендуем почитать
Pro Git

Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.


Java 7

Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.


MFC и OpenGL

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Симуляция частичной специализации

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Обработка событий в С++

В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.


Питон — модули, пакеты, классы, экземпляры

Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.