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

Скотт Мейерс

template<typename... Ts>              // Эти троеточия

void processVals(const Ts&... params) // в исходном

{                                     // тексте С++

 …                                    // Это троеточие озна-

                                      // чает какой-то код

}

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

Когда объект инициализирован другим объектом того же типа, новый объект является копией инициализирующего объекта, даже если копия создается с помощью перемещающего конструктора. К сожалению, в С++ нет никакой терминологии, которая позволяла бы различать объекты, созданные с помощью копирующих и перемещающих конструкторов:

void someFunc(Widget w);  // Параметр w функции someFunc

                          // передается по значению

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

someFunc(wid);            // В этом вызове someFunc w

                          // является копией wid, созданной

                          // копирующим конструктором

someFunc(std::move(wid)); // В этом вызове SomeFunc w

                          // является копией wid, созданной

                          // перемещающим конструктором

Копии rvalue в общем случае конструируются перемещением, в то время как копии lvalue обычно конструируются копированием. Следствием является то, что если вы знаете только то, что объект является копией другого объекта, то невозможно сказать, насколько дорогостоящим является создание копии. В приведенном выше коде, например, нет возможности сказать, насколько дорогостоящим является создание параметра w, без знания того, какое значение передано функции someFunc — rvalue или lvalue. (Вы также должны знать стоимости перемещения и копирования Widget.)

В вызове функции выражения, переданные в источнике вызова, являются аргументами функции. Эти аргументы используются для инициализации параметров функции. В первом вызове someFunc, показанном выше, аргументом является wid. Во втором вызове аргументом является std::move(wid). В обоих вызовах параметром является w. Разница между аргументами и параметрами важна, поскольку параметры являются lvalue, но аргументы, которыми они инициализируются, могут быть как rvalue, так и lvalue. Это особенно актуально во время прямой передачи, при которой аргумент, переданный функции, передается другой функции так, что при этом сохраняется его “правосторонность” или “левосторонность” (Прямая передача подробно рассматривается в разделе 5.8.)

Хорошо спроектированные функции безопасны с тачки зрения исключений, что означает, что они обеспечивают как минимум базовую гарантию, т.е. гарантируют, что, даже если будет сгенерировано исключение, инварианты программы останутся нетронутыми (т.е. не будут повреждены структуры данных) и не будет никаких утечек ресурсов. Функции, обеспечивающие строгую гарантию, гарантируют, что, даже если будет сгенерировано исключение, состояние программы останется тем же, что и до вызова функции.