Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - [81]
В первом примере раздела 5.4 функция >logAndAdd
является типичным представителем функций, которые могут избежать недостатков перегрузки для универсальных ссылок, просто используя разные имена для потенциальных перегрузок. Например, рассматривавшиеся перегрузки >logAndAdd
могут быть разделены на >logAndAddName
и >logAndAddNameIdx
. Увы, этот подход не будет работать для второго рассматривавшегося примера — конструктора >Person
, потому что имена конструкторов в языке зафиксированы. Кроме того, кто же захочет отказаться от перегрузки?
>const T&
Одной из альтернатив является возврат к С++98 и замена передачи универсальной ссылки передачей lvalue-ссылки на >const
. Фактически это первый подход, рассматривавшийся в разделе 5.4. Его недостаток состоит в том, что данный дизайн не столь эффективен, как нам хотелось бы. С учетом всех нынешних наших знаний о взаимодействии универсальных ссылок и перегрузки отказ от некоторой эффективности в пользу простоты может оказаться более привлекательными компромиссом, чем нам казалось изначально.
Подход, который часто позволяет добиться производительности без увеличения сложности, заключается в замене передачи параметров по ссылке передачей по значению, как бы противоестественно это ни звучало. Этот дизайн основан на совете из раздела 8.1: рассмотреть вопрос о передаче объектов по значению в случае, когда известно, что они будут копироваться. Поэтому я отложу до указанного раздела подробное обсуждение того, как все это работает и насколько эффективным является это решение. Здесь я просто покажу, как этот подход может использоваться в примере с классом >Person
:
>class Person {
>public:
> explicit Person(std::string n) // Замена конструктора с T&&;
> : name(std::move(n)) {} // о применении std::move
> // читайте в разделе 8.1
> explicit Person(int idx) // Как и ранее
> : name(nameFromIdx(idx)) {}
> …
>private:
> std::string name;
>};
Поскольку конструктора >std::string
, принимающего только целочисленное значение, нет, все аргументы типа >int
и подобных ему (например, >std::size_t
, >short
, >long
), передаваемые конструктору >Person
, будут перенаправлены к перегрузке для >int
. Аналогично все аргументы типа >std::string
(а также аргументы, из которых могут быть созданы объекты >std::string
, например литералы наподобие >"Ruth"
) будут передаваться конструктору, принимающему >std::string
. Здесь для вызывающего кода нет никаких сюрпризов. Возможно, некоторые программисты будут удивлены, что применение >0
или >NULL
в качестве нулевого указателя приведет к вызову перегрузки для >int
, но таким программистам необходимо внимательно прочесть раздел 3.2 и читать его до полного просветления в данном вопросе.
Ни передача lvalue-ссылки на >const
, ни передача по значению не предоставляют поддержку прямой передачи. Если мотивом использования универсальной ссылки является прямая передача, мы вынуждены использовать универсальную ссылку; у нас просто нет иного выбора. Тем не менее мы не хотим отказываться и от перегрузки. Если мы не отказываемся ни от перегрузок, ни от универсальных ссылок, то как же мы сможем избежать перегрузки для универсальных ссылок?
В действительности это не так трудно. Вызовы перегруженных функций разрешаются путем просмотра всех параметров всех перегрузок, а также всех аргументов в точке вызова с последующим выбором функции с наилучшим общим соответствием — с учетом всех комбинаций “параметр/аргумент”. Параметр, являющийся универсальной ссылкой, обычно обеспечивает точное соответствие для всего, что бы ни было передано, но если универсальная ссылка является частью списка параметров, содержащего другие параметры, универсальными ссылками не являющиеся, достаточно плохое соответствие этих последних параметров может привести к отказу от вызова такой перегрузки. Эта идея лежит в основе подхода диспетчеризации дескрипторов (tag dispatch), а приведенный ниже пример позволит лучше понять, о чем идет речь.
Применим этот метод к примеру >logAndAdd
из третьего фрагмента кода раздела 5.4. Чтобы вам не пришлось его искать, повторим его здесь:
>std::multiset
>template
>void logAndAdd(T&& name) // добавляет name в names
>{
> auto now = std::chrono::system_clock::now();
> log(now, "logAndAdd");
> names.emplace(std::forward
>}
Сама по себе эта функция работает отлично, но если мы добавим перегрузку, принимающую значение типа >int
, использующееся для поиска объекта по индексу, то получим проблемы, описанные в разделе 5.4. Цель данного раздела — их избежать. Вместо добавления перегрузки мы реализуем >logAndAdd
заново для делегирования работы двум другим функциям: одной — для целочисленных значений, а другой — для всего прочего. Сама функция >logAndAdd
будет принимать все типы аргументов, как целочисленные, так и нет.
Эти две функции, выполняющие реальную работу, будут называться >logAndAddImpl
, т.e. мы воспользуемся перегрузкой. Одна из этих функций будет принимать универсальную ссылку. Так что у нас будет одновременно и перегрузка, и универсальные ссылки. Но каждая функция будет принимать и второй параметр, указывающий, является ли передаваемый аргумент целочисленным значением. Этот второй параметр и будет средством, избавляющим нас от падений в болото, описанное в разделе 5.4, так как он будет фактором, определяющим выбираемую перегрузку.
В этой книге известный автор Скотт Мейерс раскрывает секреты настоящих мастеров, позволяющие добиться максимальной эффективности при работе с библиотекой STL.Во многих книгах описываются возможности STL, но только в этой рассказано о том, как работать с этой библиотекой. Каждый из 50 советов книги подкреплен анализом и убедительными примерами, поэтому читатель не только узнает, как решать ту или иную задачу, но и когда следует выбирать то или иное решение — и почему именно такое.
Когда приходится инкапсулировать, то иногда лучше меньше, чем большеЯ начну со следующего утверждения: Если вы пишете функцию, которая может быть выполнена или как метод класса, или быть внешней по отношению к классу, Вы должны предпочесть ее реализацию без использования метода. Такое решение увеличивает инкапсуляцию класса. Когда Вы думаете об использовании инкапсуляции, Вы должны думать том, чтобы не использовать методы.Удивлены? Читайте дальше.
Разработчику часто требуется много сторонних инструментов, чтобы создавать и поддерживать проект. Система Git — один из таких инструментов и используется для контроля промежуточных версий вашего приложения, позволяя вам исправлять ошибки, откатывать к старой версии, разрабатывать проект в команде и сливать его потом. В книге вы узнаете об основах работы с Git: установка, ключевые команды, gitHub и многое другое.В книге рассматриваются следующие темы:основы Git;ветвление в Git;Git на сервере;распределённый Git;GitHub;инструменты Git;настройка Git;Git и другие системы контроля версий.
Рассмотрено все необходимое для разработки, компиляции, отладки и запуска приложений Java. Изложены практические приемы использования как традиционных, так и новейших конструкций объектно-ориентированного языка Java, графической библиотеки классов Swing, расширенной библиотеки Java 2D, работа со звуком, печать, способы русификации программ. Приведено полное описание нововведений Java SE 7: двоичная запись чисел, строковые варианты разветвлений, "ромбовидный оператор", NIO2, новые средства многопоточности и др.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
В книге рассказывается история главного героя, который сталкивается с различными проблемами и препятствиями на протяжении всего своего путешествия. По пути он встречает множество второстепенных персонажей, которые играют важные роли в истории. Благодаря опыту главного героя книга исследует такие темы, как любовь, потеря, надежда и стойкость. По мере того, как главный герой преодолевает свои трудности, он усваивает ценные уроки жизни и растет как личность.
Python - объектно-ориентированный язык сверхвысокого уровня. Python, в отличии от Java, не требует исключительно объектной ориентированности, но классы в Python так просто изучить и так удобно использовать, что даже новые и неискушенные пользователи быстро переходят на ОО-подход.