Скотт Мейерс - Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14
- Название:Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:2016
- Город:Москва
- ISBN:978-5-8459-2000-3
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Скотт Мейерс - Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 краткое содержание
В книге рассматриваются следующие темы. Освоение С++11 и С++14 — это больше, чем просто ознакомление с вводимыми этими стандартами возможностями (например, объявлениями типов
, семантикой перемещения, лямбда-выражениями или поддержкой многопоточности). Вопрос в том, как использовать их эффективно, чтобы создаваемые программы были корректны, эффективны и переносимы, а также чтобы их легко можно было сопровождать. Именно этим вопросам и посвящена данная книга, описывающая создание по-настоящему хорошего программного обеспечения с использованием C++11 и С++14 — т.е. с использованием современного С++.
■ Преимущества и недостатки инициализации с помощью фигурных скобок, спецификации
, прямой передачи и функций
интеллектуальных указателей
■ Связь между
,
, rvalue-ссылками и универсальными ссылками
■ Методы написания понятных, корректных,
лямбда-выражений
■ Чем
отличается от
, как они используются и как соотносятся с API параллельных вычислений С++
■ Какие из лучших методов “старого” программирования на С++ (т.е. С++98) должны быть пересмотрены при работе с современным С++
Более чем 20 лет книги
серии
являются критерием уровня книг по программированию на С++. Понятное пояснение сложного технического материала принесло ему всемирную известность. Он всегда самый желанный гость на международных конференциях, а его услуги консультанта широко востребованы во всем мире.
Скотт Мейерс Эффективный и современный С++, После изучения основ С++ я перешел к изучению того, как применять С++ в промышленном программировании, с помощью серии книг Скотта Мейерса Эффективный С++. Эффективный и современный С++ — наиболее важная из книг серии, предлагающая ключевые рекомендации, стили и идиомы, позволяющие эффективно использовать современный С++. Вы еще не купили эту книгу? Сделайте это прямо сейчас. Герб Саттер,
глава Комитета ISO по стандартизации С++, специалист в области архитектуры программного обеспечения на С++ в Microsoft
Эффективный и современный С++. 42 рекомендации по использованию С++11 и С++14 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Для подобных ситуаций, в которых перегруженная функция, принимающая универсальную ссылку, оказывается более “жадной”, чем вы хотели, но недостаточно жадной, чтобы действовать как единственная функция диспетчеризации, метод диспетчеризации дескрипторов оказывается не тем, что требуется. Вам нужна другая технология, и эта технология — std::enable_if
.
std::enable_if
дает вам возможность заставить компиляторы вести себя так, как если бы определенного шаблона не существовало. Такие шаблоны называют отключенными (disabled). По умолчанию все шаблоны включены , но шаблон, использующий std::enable_if
, включен, только если удовлетворяется условие, определенное std::enable_if
. В нашем случае мы хотели бы включить конструктор Person
с прямой передачей, только если передаваемый тип не является Person
. Если переданный тип — Person
, то мы хотели бы отключить конструктор с прямой передачей (т.e. заставить компилятор его игнорировать), поскольку при этом для обработки вызова будет применен копирующий или перемещающий конструктор, а это именно то, чего мы хотим, когда один объект типа Person
инициализируется другим объектом того же типа.
Способ выражения этой идеи не слишком сложен, но имеет отталкивающий синтаксис, в особенности если вы не встречались с ним ранее. Имеются некоторые шаблоны, располагающиеся вокруг части условия std::enable_if
, так что начнем с него. Вот объявление конструктора с прямой передачей класса Person
, который показывает не более чем необходимо для простого использования std::enable_if
. Я покажу только объявление этого конструктора, поскольку применение std::enable_if
не влияет на реализацию функции. Реализация остается той же, что и в разделе 5.4:
class Person {
public:
template
typename = typename std::enable_if<���условие>::type>
explicit Person(T&& n);
…
};
Вынужден с прискорбием сообщить, что для того, чтобы разобраться, что происходит в выделенном тексте, вам следует проконсультироваться с другими источниками информации, так как в этой книге у меня просто нет места, чтобы подробно все описать. (В процессе вашего поиска поищите как std::enable_if
, так и волшебную аббревиатуру “SFINAE”, поскольку именно эта технология позволяет работать std::enable_if
.) Здесь я хочу сосредоточиться на выражении условия, которое управляет тем, является ли конструктор включенным.
Условие, которое мы хотим указать, — что тип T
не является Person
, т.e. что шаблонизированный конструктор может быть включенным, только если T
является типом, отличным от Person
. Благодаря свойствам шаблонов мы можем определить, являются ли два типа одним и тем же ( std::is_same
), так что создается впечатление, что интересующее нас условие можно записать как !std::is_same::value
. (Обратите внимание на символ “ !
” в начале выражения. Мы хотим, чтобы типы Person
и T
не совпадали.) Это близко к тому, что нам надо, но не совсем верно, поскольку, как поясняет раздел 5.6, тип, выведенный для универсальной ссылки, инициализированной lvalue, всегда является lvalue-ссылкой. Это означает, что в коде наподобие
Person p("Nancy");
auto cloneOfP(p); // Инициализация с помощью lvalue
тип T
в универсальном конструкторе будет выведен как Person&
. Типы Person
и Person&
— разные, и результат std::is_same
отражает этот факт: значение std::is_same::value
ложно.
Если разобраться, что означает, что шаблонный конструктор в классе Person
должен быть включен только тогда, когда T
не является Person
, то мы поймем, что, глядя на T
, мы хотим игнорировать следующее.
• Ссылки. С точки зрения определения, должен ли быть включен конструктор с универсальной ссылкой, типы Person
, Person&
и Person&&
должны рассматриваться как идентичные типу Person
.
• Модификаторы const
и volatile
. С той же точки зрения типы const Person
, volatile Person
и const volatile Person
должны рассматриваться как идентичные типу Person
.
Это означает, что нам нужен способ удалить все ссылки, const
и volatile
из типа T
перед тем как выяснять, совпадает ли он с типом Person
. И вновь на выручку приходит стандартная библиотека, предоставляя шаблон std::decay
. Тип std::decay::type
представляет собой то же самое, что и тип T
, но из него удалены все ссылки и квалификаторы const
и volatile
. (Я немного вас обманул, потому что std::decay
, кроме того, превращает массивы и типы функций в указатели (см. раздел 1.1), но для наших целей можно считать, что std::decay
ведет себя так, как я описал.) Условие, которое должно выполняться для включения рассматриваемого конструктора, имеет вид
!std::is_same::type>::value
т.e. Person
не совпадает с типом T
, без учета всех ссылок и квалификаторов const
и volatile
. (Как поясняется в разделе 3.3, ключевое слово typename перед std::decay
необходимо, поскольку тип std::decay::type
зависит от параметра шаблона T
.)
Вставка этого условия в шаблон std::enable_if
выше, а также форматирование результата для того, чтобы проще понять взаимоотношения между частями кода, дает следующее объявление конструктора с прямой передачей класса Person
:
class Person {
public:
template<
typename T,
typename = typename std::enable_if<
!std::is_same
typename std::decay::type
>::value
>::type
>
explicit Person(T&& n);
};
Если вы никогда ранее не видели ничего подобного, не пугайтесь. Есть причина, по которой я оставил этот метод напоследок. Если для того, чтобы избежать смешивания универсальных ссылок и перегрузки вы можете использовать один из прочих методов (а это почти всегда возможно), вы должны это сделать. Тем не менее, если вы привыкнете к функциональному синтаксису и множеству угловых скобок, это не так плохо. Кроме того, это позволяет получить поведение, к которому вы стремитесь. С учетом приведенного выше объявления построение объекта Person
из другого объекта Person
(lvalue или rvalue, с квалификатором const
или без него, с квалификатором volatile
или без него) никогда не вызовет конструктор, принимающий универсальную ссылку.
Мы добились успеха? Дело сделано?
Пока что нет. Не спешите праздновать. Раздел 5.4 все еще посылает нам свои приветы. Нам надо заткнуть этот фонтан окончательно.
Предположим, что класс, производный от Person
, реализует операции копирования и перемещения традиционным способом:
class SpecialPerson: public Person {
public:
SpecialPerson (const SpecialPerson& rhs) // Копирующий
Интервал:
Закладка: