Эффективный и современный C++ [Скотт Мейерс] (pdf) читать постранично, страница - 3

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

особенностью этой эвристики является то, что она помогает
помнить, что тип выражения не зависит от того, является ли оно 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
void processVal s ( const Ts& . . . params )

11
11
11
11
11

Эти троеточия
в исходном
тексте С++
Это троеточие означае т как ой -то код

Объявление processVals показывает, что я использую ключевое слово typename
при объявлении параметров типов в шаблонах, но это просто мое л ичное
предпочтение; вместо него можно использовать ключевое слово class. В тех
случаях, когда я показываю код, взятый из стандарта С++, я объявляю параметры
типа с использованием ключевого слова class, поскольку так делает стандарт.
Когда объект инициализирован другим объектом того же типа, новый объект яв­
ляется копией инициализирующего объекта, даже если копия создается с помощью пе­
ремещающего конструктора. К сожалению, в С++ нет никакой терминологии, которая
позволяла бы различать объекты, созданные с помощью копирующих и перемещающих
конструкторов:
Введение

17

void someFunc ( Widget w) ;

/ / Параметр w функции someFunc
11 передается по значению

Widget wid;

11 wid - объект класса Widget

someFunc ( wid) ;

11 В этом вызове someFunc w
/ / является копией wid , созданной
/ / копирующим конструктором

someFunc ( st d : : move ( wid) ) ; / / В этом вызове SomeFunc w
/ / является копией wid, созданной
/ / перемещающим конструктором

Копии rvalue в общем случае конструируются перемещением, в то время как копии
lvalue обычно конструируются копированием. Следствием является то, что если вы зна­
ете только то, что объект является копией друrого объекта, то невозможно сказать, на­
сколько дорогостоящим является создание копии. В приведенном выше коде, например,
нет возможности сказать, насколько дорогостоящим является создание параметра w, без
знания того, какое значение передано функции someFunc
rvalue или lvalue. (Вы также
должны знать стоимости перемещения и копирования Widget.)
В вызове функции выражения, переданные в источнике вызова, являются аргумен­
тами функции. Эти аргументы используются для инициализации параметров функции.
В первом вызове someFunc, показанном выше, аргументом является wid. Во втором вы­
зове аргументом является std ::move ( w i d) . В обоих вызовах параметром является w.
Разница между аргументами и параметрами важна, поскольку параметры являются lvalue,
но аргументы, которыми они инициализируются, могут быть как rvalue, так и lvalue. Это
особенно актуально во время прямой передачи, при которой аргумент, переданный функ­
ции, передается другой функции так, что при этом сохраняется его "правосторонность"
или "левосторонность". (Прямая передача подробно рассматривается в разделе 5.8.)
Хорошо спроектированные функции безопасны с тачки зрения исключений, что озна­
чает, что они обеспечивают как минимум базовую гарантию, т.е. гарантируют, что, даже
если будет сгенерировано исключение,