Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - страница 6
>
>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.)
Хорошо спроектированные функции безопасны с тачки зрения исключений, что означает, что они обеспечивают как минимум базовую гарантию, т.е. гарантируют, что, даже если будет сгенерировано исключение, инварианты программы останутся нетронутыми (т.е. не будут повреждены структуры данных) и не будет никаких утечек ресурсов. Функции, обеспечивающие строгую гарантию, гарантируют, что, даже если будет сгенерировано исключение, состояние программы останется тем же, что и до вызова функции.
Говоря о функциональном объекте, я обычно имею в виду объект типа, поддерживающего функцию-член >operator()
. Другими словами, это объект, действующий, как функция. Иногда я использую термин в несколько более общем смысле для обозначения чего угодно, что может быть вызвано с использованием синтаксиса вызова функции, не являющейся членом (т.е. >functionName(arguments)
). Это более широкое определение охватывает не только объекты, поддерживающие operator(), но и функции и указатели на функции в стиле С. (Более узкое определение происходит из С++98, более широкое — из C++11.) Дальнейшее обобщение путем добавления указателей на функции-члены дает то, что известно как вызываемый объект (callable object). Вообще говоря, можно игнорировать эти тонкие отличия и просто рассматривать функциональные и вызываемые объекты как сущности в С++, которые могут быть вызваны с помощью некоторой разновидности синтаксиса вызова функции.