С. Мейерс - Эффективный и современный C++ (1114942), страница 3
Текст из файла (страница 3)
Более того, этиответы не зависят от платформы и соответствуют стандарту. Это книга о переносимом С++.Разделы книги представляют собой рекомендации, а не жесткие правила, посколькурекомендации имеют исключения. Наиболее важной частью каждого раздела являетсяне предлагаемая в нем рекомендация, а ее обоснование. Прочитав раздел, вы сможетесами определить, оправдывают ли обстоятельства вашего конкретного проекта отходот данной рекомендации. Истинная цель книги не в том, чтобы рассказать вам, как надопоступать или как поступать не надо, а в том, чтобы обеспечить вас более глубоким пониманием, как та или иная концепция работает в С++ 1 1 и С++ 1 4.Терминоnо r ия и соrnашенияЧтобы мы правильно понимали друг друга, важно согласовать используемую терминологию, начиная, как ни странно это звучит, с термина "С++".
Есть четыре официальные версии С++, и каждая именуется с использованием года принятия соответствующегостандарта ISO: С++98, С++ОЗ, C++l 1 и С++14. С+ +98 и С++ОЗ отличаются один от другого только техническими деталями, так что в этой книге обе версии я называю как С++98.Говоря о С++ 1 1 , я подразумеваю и С++ 1 1 , и С++ 1 4, поскольку С++ 14 является надмножеством С++ 1 1 . Когда я пишу "С++ 1 4': я имею в виду конкретно С++ 14. А если я простоупоминаю С++, я делаю утверждение, которое относится ко всем версиям языка.Использованный терминПодразумеваемая версия(++Все(++98с++98ис++03(++11(++11и(++14(++14(++14В результате я мог бы сказать, что в С++ придается большое значение эффективности(справедливо для всех версий), в С++98 отсутствует поддержка параллелизма (справедливо только для С++98 и С++ОЗ), С++ 1 1 поддерживает лямбда-выражения (справедливодля C++l l и С++14) и С++14 предлагает обобщен ный вывод возвращаемого типа функции (справедливо только для С++ 1 4).Наиболее важной особенностью С++ 1 1 , вероятно, является семантика перемещения,а основой семантики перемещения является отличие rvа/ие-выражени й от /vа/uе-выражений.
Поэтому rvalue указывают объекты, которые могут быть перемещены, в то времякак lvalue в общем случае перемещены быть не могут. Концептуально (хотя и не всегдана практике), rvalue соответствуют временным объектам, возвращаемым из функций,в то время как lvalue соответствуют объектам, на которые вы можете ссылаться по имени, следуя указателю или lvalue-ccылкe.Полезной эвристикой для выяснения, является ли выражение lvalue, является ответна вопрос, можно ли получить его адрес.
Если можно, то обычно это lvalue. Если нет, этообычно rvalue. Приятной особенностью этой эвристики является то, что она помогаетпомнить, что тип выражения не зависит от того, является ли оно lvalue или rvalue. Иначеговоря, для данного типа Т можно иметь как lvalue типа Т, так и rvalue типа Т.
Особенноважно помнить это, когда мы имеем дело с параметром rvalue ссылочного типа, поскольку сам по себе параметр является lvalue:class Widget {puЫ i c :Widget (Widqet&& rhs); // rhs является lvalue, хотя/ / и имеет ссьu�очный тип rvalue};16ВведениеЗдесь совершенно корректным является взятие адреса rhs в перемещающем конструкторе Widget, так что rhs представляет собой lvalue, несмотря на то что его тип - ссылкаrvalue. (По сходным причинам все параметры являются lvalue.)Этот фрагмент кода демонстрирует несколько соглашений, которым я обычно следую.•Имя класса - Widget.
Я использую слово Widget, когда хочу сослаться на произвольный пользовательский тип. Если только мне не надо показать конкретные детали класса, я использую имя Widget, не объявляя его.•Я использую имя параметра rhs ("right-hand side'; правая сторона). Это предпочитаемое мною имя параметра для операций перемещения (например, перемещающегоконструктора и оператора перемещающего присваивания) и операций копирования(например, копирующего конструктора и оператора копирующего присваивания). Ятакже использую его в качестве правого параметра бинарных операторов:Matrix operator+ ( const Matrix& lhs , const Matrix& rhз);Я надеюсь, для вас не станет сюрпризом, что lhs означает "left-hand side" (леваясторона).•использую специальное форматирование для частей кода или частей комментариев, чтобы привлечь к ним ваше внимание.
В перемещающем конструкторе Widgetвыше я подчеркнул объявление rhs и часть комментария, указывающего, что rhsпредставляет собой lvalue. Выделенный код сам по себе не является ни плохим, нихорошим. Это просто код, на который вы должны обратить внимание.•Я использую ".. . '; чтобы указать "здесь находится прочий код': Такое "узкое" троеточие отличается от широкого ... ·; используемого в исходных текстах шаблоновс переменным количеством параметров в С++ 1 1 . Это кажется запутанным, но на самом деле это не так. Вот пример.Я"template<t ypename .
. . Ts>void processVal s ( const Ts& . . . params )1111111111Эти троеточияв исходномтексте С++Это троеточие означае т как ой -то кодОбъявление processVals показывает, что я использую ключевое слово typenameпри объявлении параметров типов в шаблонах, но это просто мое л ичноепредпочтение; вместо него можно использовать ключевое слово class. В техслучаях, когда я показываю код, взятый из стандарта С++, я объявляю параметрытипа с использованием ключевого слова class, поскольку так делает стандарт.Когда объект инициализирован другим объектом того же типа, новый объект является копией инициализирующего объекта, даже если копия создается с помощью перемещающего конструктора. К сожалению, в С++ нет никакой терминологии, котораяпозволяла бы различать объекты, созданные с помощью копирующих и перемещающихконструкторов:Введение17void someFunc ( Widget w) ;/ / Параметр w функции someFunc11 передается по значениюWidget wid;11 wid - объект класса WidgetsomeFunc ( wid) ;11 В этом вызове someFunc w/ / является копией wid , созданной/ / копирующим конструкторомsomeFunc ( st d : : move ( wid) ) ; / / В этом вызове SomeFunc w/ / является копией wid, созданной/ / перемещающим конструкторомКопии rvalue в общем случае конструируются перемещением, в то время как копииlvalue обычно конструируются копированием.
Следствием является то, что если вы знаете только то, что объект является копией друrого объекта, то невозможно сказать, насколько дорогостоящим является создание копии. В приведенном выше коде, например,нет возможности сказать, насколько дорогостоящим является создание параметра w, беззнания того, какое значение передано функции someFuncrvalue или lvalue.
(Вы такжедолжны знать стоимости перемещения и копирования Widget.)В вызове функции выражения, переданные в источнике вызова, являются аргументами функции. Эти аргументы используются для инициализации параметров функции.В первом вызове someFunc, показанном выше, аргументом является wid. Во втором вызове аргументом является std ::move ( w i d) . В обоих вызовах параметром является w.Разница между аргументами и параметрами важна, поскольку параметры являются lvalue,но аргументы, которыми они инициализируются, могут быть как rvalue, так и lvalue.
Этоособенно актуально во время прямой передачи, при которой аргумент, переданный функции, передается другой функции так, что при этом сохраняется его "правосторонность"или "левосторонность". (Прямая передача подробно рассматривается в разделе 5.8.)Хорошо спроектированные функции безопасны с тачки зрения исключений, что означает, что они обеспечивают как минимум базовую гарантию, т.е. гарантируют, что, дажеесли будет сгенерировано исключение, инварианты программы останутся нетронутыми(т.е. не будут повреждены структуры данных) и не будет никаких утечек ресурсов. Функции, обеспечивающие строгую гарантию, гарантируют, что, даже если будет сгенерировано исключение, состояние программы останется тем же, что и до вызова функции.Говоря о функциональном объекте, я обычно имею в виду объект типа, поддерживающего функцию-член operator ().
Другими словами, это объект, действующий, какфункция. Иногда я использую термин в несколько более общем смысле для обозначениячего уrодно, что может быть вызвано с использованием синтаксиса вызова функции, неявляющейся членом (т.е. function Name (arguments) ). Это более широкое определение охватывает не только объекты, поддерживающие operator (), но и функции и указатели на функции в стиле С.