Майерс эффективное использование c. Скотт Мейерс - Эффективное использование STL

Прочие модели 08.04.2019
(1959-04-09 ) (59 лет)

Скотт Дуглас Майерс (англ. Scott Meyers ) - эксперт по языку программирования C++ , консультант по разработке программного обеспечения и автор серии книг «Эффективное использование C++». Он получил степень бакалавра по биологии и магистра по вычислительной технике в Стэнфордском университете и степень доктора компьютерных наук в Брауновском университете (1985) . Скотт Майерс является постоянным автором в «The С/С++ Users Journal» . В 2009 году он получил премию «Dr. Dobb’s Excellence in Programming Award» , как человек внесший значительный вклад в развитие программного обеспечения в духе новаторства и сотрудничества.

Библиография

  • Майерс С. Эффективное использование STL. - СПб: Питер, 2002. - 224 с. - (Библиотека программиста). - ISBN 5-94723-382-7 .
  • Майерс С. Эффективное использование С++. 35 новых способов улучшить стиль программирования. - СПб: Питер, 2006. - 224 с. - (Библиотека программиста). - ISBN 5-469-01215-8 .
  • Майерс С. Эффективное использование C++. 50 рекомендаций по улучшению ваших программ и проектов. - СПб: Питер, 2006. - 240 с. - (Библиотека программиста). - ISBN 5-469-01213-1 .
  • Скотт Мэйерс. Эффективное использование C++. 55 верных советов улучшить структуру и код ваших программ. - Москва: ДМК-Пресс, 2006. - 300 с. - (Профессиональная серия от Addison-Wesley). - ISBN 5-94074-304-8 , 0-321-33487-6.
  • Scott Meyers. Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14. - O"Reilly Media, 2014. - 336 с. - ISBN 978-1491903995 , 1491903996.
  • Скотт Мейерс. Эффективный и современный С++: 42 рекомендации по использованию C++11 и C++14. - Вильямс, 2016. - 304 с. - ISBN 978-5-8459-2000-3 .

Напишите отзыв о статье "Майерс, Скотт"

Примечания

Ссылки

  • (англ.)
  • Алексей Доля. . Ф-Центр (28 октября 2003). Проверено 3 сентября 2010. .

Отрывок, характеризующий Майерс, Скотт

Страшное известие о Бородинском сражении, о наших потерях убитыми и ранеными, а еще более страшное известие о потере Москвы были получены в Воронеже в половине сентября. Княжна Марья, узнав только из газет о ране брата и не имея о нем никаких определенных сведений, собралась ехать отыскивать князя Андрея, как слышал Николай (сам же он не видал ее).
Получив известие о Бородинском сражении и об оставлении Москвы, Ростов не то чтобы испытывал отчаяние, злобу или месть и тому подобные чувства, но ему вдруг все стало скучно, досадно в Воронеже, все как то совестно и неловко. Ему казались притворными все разговоры, которые он слышал; он не знал, как судить про все это, и чувствовал, что только в полку все ему опять станет ясно. Он торопился окончанием покупки лошадей и часто несправедливо приходил в горячность с своим слугой и вахмистром.
Несколько дней перед отъездом Ростова в соборе было назначено молебствие по случаю победы, одержанной русскими войсками, и Николай поехал к обедне. Он стал несколько позади губернатора и с служебной степенностью, размышляя о самых разнообразных предметах, выстоял службу. Когда молебствие кончилось, губернаторша подозвала его к себе.
– Ты видел княжну? – сказала она, головой указывая на даму в черном, стоявшую за клиросом.
Николай тотчас же узнал княжну Марью не столько по профилю ее, который виднелся из под шляпы, сколько по тому чувству осторожности, страха и жалости, которое тотчас же охватило его. Княжна Марья, очевидно погруженная в свои мысли, делала последние кресты перед выходом из церкви.
Николай с удивлением смотрел на ее лицо. Это было то же лицо, которое он видел прежде, то же было в нем общее выражение тонкой, внутренней, духовной работы; но теперь оно было совершенно иначе освещено. Трогательное выражение печали, мольбы и надежды было на нем. Как и прежде бывало с Николаем в ее присутствии, он, не дожидаясь совета губернаторши подойти к ней, не спрашивая себя, хорошо ли, прилично ли или нет будет его обращение к ней здесь, в церкви, подошел к ней и сказал, что он слышал о ее горе и всей душой соболезнует ему. Едва только она услыхала его голос, как вдруг яркий свет загорелся в ее лице, освещая в одно и то же время и печаль ее, и радость.
– Я одно хотел вам сказать, княжна, – сказал Ростов, – это то, что ежели бы князь Андрей Николаевич не был бы жив, то, как полковой командир, в газетах это сейчас было бы объявлено.
Княжна смотрела на него, не понимая его слов, но радуясь выражению сочувствующего страдания, которое было в его лице.
– И я столько примеров знаю, что рана осколком (в газетах сказано гранатой) бывает или смертельна сейчас же, или, напротив, очень легкая, – говорил Николай. – Надо надеяться на лучшее, и я уверен…
Княжна Марья перебила его.
– О, это было бы так ужа… – начала она и, не договорив от волнения, грациозным движением (как и все, что она делала при нем) наклонив голову и благодарно взглянув на него, пошла за теткой.

Эта подборка предназначена для тех программистов, которые уже умеют разрабатывать приложения на языке C++. С помощью представленных книг вы сможете отточить свои навыки и научиться некоторым техникам, которые позволять вам писать эффективный и быстрый код. Приятного погружения в океан знаний!

С чего начинается знакомство с книгой? Конечно, с заголовка. Вам не показался он смутно знакомым? Если нет, то советую почитать про загадочное число «42» . В книге вы найдете 42 (как неожиданно!) рекомендации по написанию кода, которые помогут избежать множество ошибок. Автор Андрей Карпов имеет огромный опыт в анализе кода с помощью статического анализатора PVS-Studio. Он обнаружил, что большая часть ошибок, которые находил анализатор, достаточно типичны и повторяются из одного проекта в другой. Так он решил написать данное руководство, чтобы помочь разработчикам, пишущим на языке C++, писать более совершенный код.

Эта книга была написана как обязательная к прочтению для программистов на C++. Ранние издания были нацелены на специалистов, пришедших с С, третье издание поменяло свой курс на мигрирующих с Java и подобных ей языков. Содержит около 50 легко запоминающихся правил, написанных и объясненных в доступной форме.

Скотт Мейерс задумывал эту книгу как продолжение предыдущей. Описанные в новой книге приемы по сравнению с оной не столь важны, но ознакомиться с ними несомненно стоит. Автор, опираясь на свой многолетний опыт, дает множество советов по написании эффективных программ на языке C++. В частности, вы сможете ознакомиться с примерами обработки исключений и анализом их влияния на структуру и поведение классов и функций С++, а также вариантами практического применения новых возможностей языка, таких как тип bool , ключевые слова mutable и explicit , пространства имен, шаблоны функций членов, стандартная библиотека шаблонов и многое другое.

В отличие от двух предыдущих книг, в этой Скотт Мейерс рассказывает обо всех преимуществах использования «фишек» современных стандартов C++11 и C++14. В первую очередь книга подойдет для тех разработчиков, которые хотят перейти с устаревших стандартов, таких как C++03 и более ранних, на современные и научиться эффективно использовать все их функции. В книге подробно освещаются следующие темы:

  • преимущества и недостатки инициализации с помощью фигурных скобок, спецификации noexcept, прямой передачи и функций make интеллектуальных указателей;
  • связь между std::move , std::forward , rvalue-ссылками и универсальными ссылками;
  • методы написания понятных, корректных, эффективных лямбда-выражений;
  • чем std::atomic отличается от volatile , как они используются и как соотносятся с API параллельных вычислений C++;
  • какие из лучших методов «устаревшего» программирования на C++ (т.е. C++98) должны быть пересмотрены при работе с современным C++.

В отличие от других руководств по оптимизации кода, представленных в этой подборке, эта книга делает акцент на оптимизации кода на достаточно низком уровне: компилятора и микропроцессора. Пособие является первым из серии книг про низкоуровневую оптимизацию. Автор рассматривает архитектуру x86 для процессоров Intel, AMD и VIA, включая их современные 64-битные версии. Напомним лишь, что к низкоуровневой оптимизации следует прибегать, только если другие способы улучшения кода не дают ощутимый результат.

В этой книге Скотт Мейерс привел множество приемов работы со стандартной библиотекой шаблонов STL. Эти рекомендации подкреплены подробным анализом и качественными примерами, поэтому читателю будет легче освоить STL и научиться использовать библиотеку в своих проектах.

Книга от издательства O’Reilly, что уже говорит о ее высоком качестве. В ней вы найдете ответы на многие вопросы о современном C++, например:

  • какие «фишки» C++ полезны в облачных вычислениях;
  • почему данный язык является единственным языком высокого уровня, использующимся для разработки приложений в трех мобильных платформах: iOS, Android, Windows;
  • какие изменения языка ожидаются в следующих обновлениях стандарта языка.

Книга представляет собой сборник задач. Она содержит подробное обсуждение управления ресурсами и безопасностью исключений в С++. В материале рассматриваются идиомы RAII и pimpl, а также такие темы, как проверка имен, дизайн классов и модель памяти С++.

Книга является продолжением труда «Решение сложных задач на С++». В ней вы можете найти материал по дженерик-программированию, оптимизации и управлению ресурсами. Эта книга - отличный пример того, как надо писать модульный код в С++, используя non-member функции и принцип единой ответственности. Рассчитана на разработчиков с глубокими познаниями в C++.

«Стандарты» здесь - это не «сколько пробелов я должен ставить перед новой строчкой?», а советы по написанию кода, идиомы и чаще всего встречающиеся грабли, которые помогут вам писать корректный, понятный и эффективный код на С++. Книга дает много полезных рекомендаций как для опытных программистов, так и для разработчиков профессионального уровня. Вторые могут взять за основу материалы из этой книги и разработать собственные стандарты программирования для собственной команды.

Данное пособие содержит в себе основные рекомендации по написанию проектов на языке C++, учитывающее как уже существующие стандарты (C++17, C++14, C++11), так и ожидаемые улучшения. Фокус материала сосредоточен на таких темах, как интерфейс, управление ресурсами и памятью, а также параллелизм. Рекомендации не затрагивают такие моменты, как стиль отступов, названия переменных, функций, классов, поскольку достаточно часто в компаниях, занимающихся разработкой, определены для этого собственные стандарты. Материал постоянно обновляется, так что вы всегда получите актуальную информацию прямо из рук создателя C++ - Бьерна Страуструпа.

Официальное пособие от компании Google по стилизации кода. Данные рекомендации используются в большинстве open source проектов поискового гиганта, написанных на C++. Google обозначила несколько главных принципов своего руководства:

  • Правила должны быть применимыми на практике.
  • Код должен быть удобочитаемым, ведь его будут изучать и другие люди.
  • Написанный код должен соответствовать уже существующему.
  • При необходимости следует ориентироваться на рекомендации сообщества разработчиков C++.
  • Избегать опасных конструкций.
  • Избегать конструкций, которые большинство C++-программистов посчитали бы сложными.
  • Приступать к оптимизации кода только в случае необходимости.

Это книга о шаблонах в том виде, в котором они существовали до С++11. Она покрывает все от самых начал до продвинутого шаблонного метапрограммирования, подробно объясняет, как работают шаблоны, обсуждает некоторые распространенные грабли. В приложениях несколько замечательных извлечений из правила одного определения (англ. ODR).

Как известно, программы для встроенных систем и микроконтроллеров разрабатывают в подавляющем большинстве случаев на языке Си. Он считается наиболее быстрым и приближенным к «железу» среди всех языков высокого уровня. Автор этой книги стремится развеять это установившееся понятие и рассказывает о разработке подобных программ на языке C++. Со времен C++98 язык получил существенные изменения, стал более удобным, безопасным, а самое главное - быстрым. Последние исследования говорят о том, что код, написанный на современном C++, по времени исполнения ничтожно мало отличается от аналогичного кода, написанного на Си, зато удобство работы и скорость разработки программы возрастает в разы. Это особенно актуально для систем реального времени. Поэтому C++ имеет все шансы стать языком номер один для разработки под микроконтроллеры.

Понравилась вам подборка? Или вы знаете еще несколько крутых книжек? Делитесь с нами своим мнением в комментариях.

Последние годы он безусловно является писателем №1 «про это», кроме того он блестящий лектор и каждая его новая книга просто обречена быть прочитана пишущими на С++. Более того, именно такую книгу я ждал давно, вышел стандарт С++11, за ним С++14, уже виднеется впереди С++17, язык стремительно меняется, однако нигде так и не были описаны все изменения в целом, взаимосвязи между ними, опасные места и рекомендуемые паттерны.

Тем не менее, регулярно просматривая Хабр, я так и не нашел публикации о новой книге, похоже придется писать самому. На полноценный перевод меня конечно не хватит, поэтому я решил сделать краткую выжимку, скромно назвав ее аннотацией. Еще я взял на себя смелость перегруппировать материал, мне кажется для короткого пересказа такой порядок подходит лучше. Все примеры кода взяты прямо из книги, изредка с моими дополнениями.

Одно предупреждение: Майерс не описывает синтакс , предполагается что читатель знает ключевые слова, как написать лямбда-выражение и т.д. Так что если кто-то решит начать изучение С++11/14 с этой книги, ему придется использовать дополнительные материалы для справки. Впрочем, это не проблема, все гуглится в один клик.

От С++98 к С++11/14. Галопом по всем новинкам

auto - на первый взгляд просто огромная ложка синтаксического сахара, которая однако способна изменить если не суть то вид С++ кода. Оказывается Страуструп предполагал ввести это ключевое слово (определенное, но бесполезное в С) в нынешнем значении еще в 1983 г., но отказался от этой идеи под давлением С-сообщества. Посмотрите, насколько это меняет код:

Template void dwim(It b, It e) { while(b != e) { typename std::iterator_traits::value_type value=*b; .... } } template void dwim(It b, It e) { while(b != e) { auto value=*b; ... } }
Второй пример не просто короче, он прячет совершенно здесь ненужный точный тип выражения *b, между прочим, в точном соответствии с канонами классического, еще дошаблонного, ООП. Более того, по сути выражение std::iterator_traits<It>::value_type - не более чем гениальный костыль, придуманный на заре STL для определения типа получающегося при разыменовании итератора, первый вариант будет работать только с типом для которого определена специализация iterator_traits<>, а вот для второго нужен лишь operator*(). Долой костыли!

Не убеждает? Вот еще пример, на мой взгляд просто убийственный:

Std::unorderd_map m; for(std::pair& p: m) { ... }
Этот код не компилируется,

пруф

auto1.cc:8:38: error: invalid initialization of reference of type std::pair<std::basic_string<char>, int>& from expression of type std::pair<const std::basic_string<char>, int>

Дело в том что правильный тип для std::unordered_map это std::pair<const std::string,int>, очевидно что ключ обязан быть константой, но гораздо проще использовать auto чем держать точный тип выражения в голове.
Еще несколько моментов, которые придают строгости языку:

Int x1=1; //1 корректно int x2; //2 а инициализовать то забыли! auto x3=1; //3 корректно auto x4; //4 ошибка! компилятор не пропустит std::vector v; unsigned x5=v.size(); //5 должно быть size_t, возможна потеря данных auto x6=v.size(); //6 корректно int f(); int x7=f(); //7 а что если сигнатура f() изменится? auto x8=f(); //8 корректно
Как видно из этих примеров, систематическое использование auto может сэкономить немало нервов при отладке.

И, наконец, там где без auto просто нельзя, лямбда-выражения:

Auto derefUPLess= (const std::unique_ptr& p1, const std::unique_ptr& p2) { return *p1 < *p2; };
В этом случае точный тип derefUPLess известен только компилятору, его просто невозможно сохранить в переменной не используя auto . Конечно возможно написать так:

Std::function&, const std::unique_ptr&)> derefUPLess= (const std::unique_ptr& p1, const std::unique_ptr& p2) { return *p1 < *p2; };
однако std::function<> и лямбда не один и тот же тип , значит будет вызываться конструктор, возможно с выделением памяти на куче, кроме того вызов std::function<> гарантированно дороже чем вызов лямбда -функции непосредатвенно.
И напоследок - ложка дегтя, auto работает по другому при инициализации через фигурные скобки:

Int x1=1; int x2(1); int x3{1}; int x4={1};
все эти выражения совершенно эквивалентны, однако:
auto x1=1; auto x2(1); auto x3{1}; auto x4={1};
x1 и x2 будут иметь тип int , однако x3 и x4 будут иметь другой тип, std::initializer_list<int> . Как только auto встречает {} инициализатор, она возвращает внутренний тип С++ для таких конструкций - std::initializer_list<> . Почему это так, даже Майерс признается что не знает, я тем более гадать не буду.

decltype - здесь все более-менее просто, эта конструкция была добавлена чтобы удобнее писать шаблоны, в частности функции с возвращаемым типом зависящим от параметра шаблона:

Template auto access(Container& c, Index i) -> decltype(c[i]) { .... return c[i]; }
Здесь auto просто указывает что возвращаемый тип будет указан после имени функции, а decltype() определяет тип возвращаемого значения, как правило ссылку на i-ый элемент контейнера, однако в общем случае именно то что возвращает c[i], что бы это ни было.
uniform initialization - как видно из названия в новом стандарте постарались ввести универсальный способ инициализации переменных, и это прекрасно, например теперь можно писать так:

Std::vector v{1,2,3}; // или даже так sockaddr_in sa={AF_INET, htons(80), inet_addr("127.0.0.1")};
более того, используя фигурные скобки можно даже инициализовать нестатические члены класса (обычные скобки не работают):

Class Widget { ... int x{0}; int y{0}; int z{0}; };
Еще это наконец-то прячет в чулан вечнозеленые грабли которые вечно валялись под ногами, особенно досаждая разработчикам шаблонов:

Widget w1(); // это не вызов конструктора без параметров, // это декларация функции Widget w2{}; // а вот это именно то что я имел ввиду
И еще один шаг к строгости языка, новая инициализация предотвращает пребразование типов с потерей точности (narrowing conversion):

Double a=1, b=2; int x=a+b; // fine int y={a+b}; // error
Однако..., все равно не покидает ощущение что что-то пошло не так. Во первых, там где задействованы фигурные скобки, инициализация всегда происходит через внутренний тип std::initializer_list<> , но, по непонятной причине, если класс определяет один из конструкторов с таким параметром, этот конструктор всегда предпочитается компилятором . Например:

Class Widget { Widget(int, int); Widget(std::initializer_list); }; Widget w1(0, 0); // calls ctor #1 Widget w2{0, 0}; // calls ctor #2 !?
Вопреки всякой очевидности во втором случае компилятор проигнорирует идеально подходящий конструктор_1 и вызовет конструктор_2, преобразовав int в double. Кстати, если поменять местами типы int и double в определении класса, то код вообще перестанет компилироваться потому что конверсия { double, double } в std::initializer_list<int> происходит с потерей точности.

Эта коллизия может произойти с любым кодом уже сейчас, по правилам С++11.
std::vector(10, 20) создает обьект из 10 элементов, тогда как
std::vector{10, 20} создает обьект только из двух элементов.
Сверху это все украсим веточкой укропа - для copy-конструкторов и move-конструкторов это правило не работает:

Class Widget { Widget(); Widget(const Widget&); Widget(Widget&&); Widget(std::initializer_list); operator int() const; }; Widget w1{}; Widget w2{w1}; Widget w3{std::move(w1)};
Буквально следуя букве закона следовало бы ожидать что компилятор выберет конструктор с параметром std::initializer_list а фактические параметры будут преобразованы через оператор int(), так ведь нет! В данном случае (copy/move constructor) вызываются именно конструкторы копий.

В общем рекомендация всегда использовать какой-то один тип скобок, круглые или фигурные, решительно не работает. Майерс советует придерживаться одного способа, применяя другой только там где необходимо, сам он склоняется к круглым скобкам, в чем я с ним согласен. Остается однако проблема с шаблонами, где то что должно быть вызвано определяется параметрами шаблона… Ну, по крайней мере С++ остается нескучным языком.

nullptr - тут даже говорить особо не о чем, очевидно что NULL так же как значение 0 не являются указателями , что приводит к многочисленным ошибкам при вызове перегруженных функций и реализации шаблонов. При этом nullptr является указателем и ни к каким ошибкам не приводит.
alias declaration против typedef
Вместо привычного обьявления типов
typedef std::unique_ptr> UPtrMapSS;
предлагается использовать вот такую конструкцию
using UPtrMapSS=std::unique_ptr>;
эти два выражения абсолютно эквивалентны, однако история на этом не кончается, синонимы (aliases) могут использоваться как шаблоны (alias templates) и это придает им дополнительную гибкость
template using MyAllocList=std::list>; MyAllocList lw;
В C++98 для создания такой конструкции MyAllocList пришлось бы обьявить шаблонной структурой, продекларировать тип внутри нее и использовать вот так:
MyAllocList::type lw;
но история продолжается. Если мы используем тип обьявленный через typedef как зависимый тип внутри шаблонного класса, нам приходится использовать дополнительные ключевые слова
template class Widget { typename MyAllocList::type lw; ...
в новом синтаксе все гораздо проще
template class Widget { MyAllocList lw; ...
В общем, метапрограммирование обещает быть гораздо более легким с этой синтаксической конструкцией. Более того, начиная с С++14 в <type_traits> вводятся соответствующие синонимы, то есть вместо привычного
typename remove_const<...>::type // можно писать remove_const_t<...>
Использование синонимов - крайне полезная привычка, которую стоит начать в себе культивировать прямо сейчас. В свое время typedef безжалостно расправился с макросами, мы не забудем, не простим и отплатим ему той же монетой.
scoped enums - еще один шаг к внутренней стройности языка. Дело в том что классические перечисления (enums ) обьявлялись внутри блока, однако их видимость (scope ) оставалась глобальной.
enum Color { black, white, red };
black, white и red видимымы в том же блоке что и Color, что приводит к конфликтам и засорению пространства имен. Новый синтакс:
enum class Color { black, white, red }; Color c=Color::white;
выглядит гораздо элегантнее. Только одно но - одновременно убрали автоматическое приведение перечислений к целым типам
int x=Color::red; // ошибка int y=static_cast(Color::white); // ok
к строгости языка это безусловно только добавляет, однако в подавляющем большинстве кода который я видел enums так или иначе конвертируются в int , хотя бы для переачи в switch или вывода в std::cout .
override, delete и default - новые полезные слова при обьявлении функций.
override сигнализирует компилятору что данная виртуальная функция-член класса должна перекрыть (override ) некую функцию базового класса и, если подходящего варианта не находится, он любезно сообщит нам об ошибке. Все наверное сталкивались с ситуацией когда случайная опечатка или изменение сигнатуры превращает виртуальную функцию в обычную, самое неприятное что все прекрасно компилируется, но работает как-то не так. Так вот, больше этого не будет. Решительно рекомендуется к использованию.
delete - призвано заменить старый (и красивый) трюк с приватным обьявлением конструктора по умолчанию и оператора присвоения. Выглядит более последовательно, но не только. Этот прием можно применять и к свободным функциям чтобы запретить нежелательные преобразования аргументов
bool isLucky(int); bool isLucky(char) =delete; bool isLucky(bool) =delete; bool isLucky(double) =delete; isLucky("a"); // error isLucky(true); // error isLucky(3.5); // error
этот же прием можно использовать и для шаблонов
template void processPointer(T*); template<> void processPointer(void*) =delete; template<> void processPointer(char*) =delete;
две последние декларации запрещают генерацию функций для некоторых типов аргумента.
default - этот модификатор заставляет компилятор генерировать автоматические функции класса, причем его действительно приходится использовать. К автоматически генерируемым функциям в С++98 относились конструктор без параметров, деструктор, копирующий конструктор и оператор присваивания, все они создавались по известным правилам в случае необходимости. В С++11 добавились перемещающий конструктор и оператор присваивания, но не только, изменились сами правила создания автоматических функций. Логика простая, автоматический деструктор вызывает по очереди деструкторы членов класса и базовых классов, копирующий/перемещающий конструктор вызывает по очереди соответствующие конструкторы своих членов и т.д. Однако, если мы вдруг решаем определить любую из этих функций вручную, значит нас это разумное поведение не устраивает и компилятор отказывается понимать наши мотивы, в таком случае перемещающие конструктор и оператор присвоения автоматически создаваться не будут. Разумеется к копирующей паре эта логика тоже применима, но решено [пока] оставить как было для обратной совместимости. То есть в С++11 имеет смысл писать как-то вот так:
class Widget { public: Widget() =default; ~Widget() =default; Widget(const Widget&) =default; Widget(Widget&&) =default; Widget& operator=(const Widget&) =default; Widget& operator=(Widget&&) =default; ... };
Если позднее вы решите определить деструктор ничего не изменится, в противном случае перемещающие функции просто исчезли бы. Код продолжал бы компилироваться, однако вызывались бы копирующие аналоги.
noexept - наконец-то стандарт признал что существующая в С++98 спецификация исключений неэффективна, признал ее использование нежелательным (deprecated ) и поставил взамен один большой красный флажок - noexcept , который декларирует что функция никогда не выбрасывает исключений. Если исключение все-таки брошено, программа гарантированно завершится, при этом, в отличие от throw() , даже стек не обязательно будет раскручен. Сам флажок оставлен из соображений эффективности, мало того что стек не нужно держать готовым к раскрутке, еще и сам генерируемый компилятором код может отличаться. Вот пример:
Widget w; std::vector v; ... v.push_back(w);
При добавлении нового элемента к вектору рано или поздно возникает ситуация когда весь внутренний буфер надо переместить в памяти, в С++98 элементы поочередно копируются . В новом стандарте было бы логично элементы вектора перемещать , это на порядок эффективнее, но есть один нюанс… Если в процессе копирования какой-то из элементов выбросит исключение, новый элемент естественно вставлен не будет, но сам вектор останется в нормальном состоянии. Если же мы элементы перемещали , то часть из них уже в новом буфере, часть еще в старом, и восстановить память в рабочее состояние уже невозможно. Выход простой, если в классе Widget перемещающий оператор присвоения продекларирован как noexcept , обьекты будут перемещаться, если нет - копироваться.

На этом закончим этот затянувшийся обзор новинок сезона

Я сознательно опустил несколько пунктов - constexpr , std::cbegin() и т.д. Они достаточно просты и говорить особенно не о чем. Вот что бы хотелось обсудить, так это тезис о том что константные функции-члены должны быть потокобезопасны, но это наоборот выходит за рамки простого добавления к синтаксу, может быть в комментариях получится.

Типы, их выведение и все с этим связанное

Выведение типов (type deduction) в С++98 использовалось исключительно в реализации шаблонов, новый стандарт добавил универсальные ссылки , ключевые слова auto и decltype . В большинстве случаев выведение интуитивно понятно, однако конфликты случаются и тогда понимание механизмов работы очень выручает. Возьмем вот такой псевдокод:
template void f(ParamType param); f(expr);
Главное здесь то что Т и ParamType в общем случае два различных типа, например ParamType может быть const T&. Точный тип Т выводится при реализации шаблона как из фактического типа expr, так и из вида ParamType, возможны несколько вариантов.
  • Самый простой случай когда ParamType не является ни указателем, ни ссылкой, тогда выражение в функцию передается по значению, из expr убираются все ссылки, const модификаторы, остается чистый тип
    template void f(T param); int x=1; const int cx=x; const int& rx=x; f(x); // во всех вызовах значение Т и param - int f(cx); f(rx);
  • Если ParamType - указатель или обычная (не универсальная) ссылка то при выведении типа Т ссылка убирается, но сохраняются const/volatile модификаторы
    template void f(T& param); int x=1; const int cx=x; const int& rx=x; f(x); // значение Т - int, param - int& f(cx); // значение Т - const int, param - const int& f(rx); // значение Т - const int, param - const int&
    интуитивно все совершенно прозрачно, мы передаем значение по ссылке как указано в шаблоне, но сохраняем модификаторы на чтение/запись чтобы не нарушить права доступа к передаваемому обьекту.
  • Если ParamType - универсальная ссылка то тип выражения зависит от типа expr. Если это lvalue то оба Т и ParamType трактуются как ссылка, а если expr - rvalue то применяются правила аналогичные обычным ссылкам:
    template void f(T&& param); int x=1; const int cx=x; const int& rx=x; // все параметры здесь - lvalue f(x); // значение Т - int&, param - int& f(cx); // значение Т - const int&, param - const int& f(rx); // значение Т - const int&, param - const int& // однако f(1); // значение Т - int, param - int&&
Для auto правила выведения типов точно такие же, в этом случае auto играет роль параметра Т, за одним исключением, которое я уже упоминал, если auto видит выражение в фигурных скобках то выводится тип std::initializer_list .
В случае decltype почти всегда возвращается именно тот тип который ему передали, в конце концов именно для этого его и придумали. Однако один нюанс все-таки существует - decltype возвращает ссылку для всех выражений отличных от просто имени, то есть:
int x=1; decltype(x); // x -имя, возвращается тип int decltype((x)); // (x) - выражение, возвращается тип int&
но вряд ли это кого-то заденет кроме библиотек активно использующих макросы.

Перечитал написанное, что-то много получается. А ведь самое интересное еще впереди, наверное лучше разбить на два поста. Продолжение следует.

Теги: Добавить метки

Эффективное использование STL

Предисловие

«...На нем не было ленточки! Не было ярлыка! Не было коробки и не было мешка!»

Доктор Зюсс, «Как Гринч украл Рождество»

Я впервые написал о STL (Standard Template Library) в 1995 году. Моя книга «More Effective C++» завершалась кратким обзором библиотеки. Но этого оказалось недостаточно, и вскоре я начал получать сообщения с вопросом, когда будет написана книга «Effective STL».

Несколько лет я сопротивлялся этой идее. Сначала я не обладал достаточным опытом программирования STL и не считал возможным давать советы. Но время шло, и на смену этой проблеме пришли другие. Бесспорно, появление библиотеки означало прорыв в области эффективной масштабируемой архитектуры, но в области использования STL возникали чисто практические проблемы, на которые было невозможно закрывать глаза. Адаптация любых программ STL, за исключением самых простейших, была сопряжена с множеством проблем, что объяснялось не только различиями в реализациях, но и разным уровнем поддержки шаблонов компиляторов. Учебники по STL были редкостью, поэтому постижение «дао программирования в STL» оказывалось задачей непростой. А как только программист справлялся с этой трудностью, возникала другая - поиск достаточно полной и точной справочной документации. Даже самая мелкая ошибка при использовании STL сопровождалась лавиной диагностических сообщений компилятора, длина которых достигала нескольких тысяч символов, причем в большинстве случаев речь шла о классах, функциях и шаблонах, не упоминавшихся в программе. При всем уважении к STL и разработчикам этой библиотеки я не решался рекомендовать ее программистам среднего звена. Я не был уверен в том, что STL можно использовать эффективно.

Затем я заметил нечто удивительное. Несмотря на все проблемы с переносом и скверное качество документации, несмотря на сообщения компилятора, напоминавшие бессмысленное нагромождение символов, многие из моих клиентов все равно работали с STL. Более того, они не просто экспериментировали с библиотекой, а использовали ее в коммерческих версиях своих программ! Для меня это было откровением. Я знал, что программы, использующие STL, отличались элегантной архитектурой, но любая библиотека, ради которой программист добровольно обрекал себя на трудности с переносом, на скверную документацию и невразумительные сообщения об ошибках, должна была обладать чем-то большим, чем хорошая архитектура. Все больше профессиональных программистов полагало, что даже плохая реализация STL лучше, чем ее полное отсутствие.

Более того, я знал, что ситуация с STL будет улучшаться. Библиотеки и компиляторы будут постепенно приближаться к требованиям Стандарта (так оно и было), появится качественная документация (см. список литературы на с. 203), а диагностика компилятора станет более вразумительной (в этой области ситуация оставляет желать лучшего, но рекомендации совета 49 помогут вам с расшифровкой сообщений). В общем, я решил внести свою лепту в движение STL. Так появилась эта книга - 50 практических советов по использованию STL в C++.

Сначала я намеревался написать книгу за вторую половину 1999 г. и даже набросал ее примерную структуру. Но потом планы изменились, я приостановил работу над книгой и разработал вводный курс по STL, который преподавался нескольким группам программистов. Примерно через год я вернулся к книге и значительно расширил материал на основании опыта, полученного за время преподавания. В книге я постарался осветить практические аспекты программирования в STL, особенно важные для профессиональных программистов.

Скотт Дуглас Мейерс Стаффорд, Орегон Апрель 2001 г.

Благодарности

За годы, которые потребовались на то, чтобы разобраться в STL, разработать учебный курс и написать эту книгу, я получил неоценимую помощь и поддержку от множества людей. Хочу особо отметить Марка Роджерса (Mark Rodgers), великодушно предложившего просматривать материалы учебного курса по мере их написания. От него я узнал об STL больше, чем от кого-либо другого. Марк также выполнял функции технического редактора этой книги, а его замечания и дополнения помогли улучшить практически весь материал.

Другим выдающимся источником информации были конференции Usenet; посвященные языку C++, особенно comp.lang.c++.moderated («clcm»), comp.std.c++ и microsoft.public.vc.stl. Свыше десяти лет участники этих и других конференций отвечали на мои вопросы и ставили задачи, над которыми мне приходилось думать. Я глубоко благодарен сообществу Usenet за помощь в работе над этой книгой и моими предыдущими публикациями по C++.

Мое понимание STL формировалось под влиянием ряда публикаций, самые важные из которых перечислены в конце книги. Особенно много полезного я почерпнул из труда Джосаттиса «The C++ Standard Library» .

Идеи и наблюдения, из которых состоит эта книга, в основном принадлежат другим авторам, хотя в ней есть и ряд моих собственных открытий. Я постарался по возможности отследить источники, из которых был почерпнут материал, но эта задача обречена на провал, поскольку информация собиралась из множества источников в течение долгого периода времени. Приведенный ниже список далеко не полон, но ничего лучше предложить не могу. Учтите, что в этом списке перечислены источники, из которых я узнавал о тех или иных идеях и приемах, а не их первооткрыватели.

В совете 1 замечание о том, что узловые контейнеры обеспечивают лучшую поддержку транзакционной семантики, позаимствовано из раздела 5.11.2 «The C++ Standard Library» . Пример использования typedef при изменении типа распределителя памяти из совета 2 был предложен Марком Роджерсом. Совет 5 вдохновлен статьeй Ривса (Reeves) «STL Gotchas» . В основу совета 8 заложен совет 37 книги Саттера «Exceptional C++» , а Кевлин Хенни (Kevlin Henney) предоставил важную информацию о проблемах, возникающих при использовании контейнеров auto_ptr. В конференциях Usenet Мэтт Остерн (Matt Austem) предоставил примеры использования распределителей памяти, включенные мной в совет 11. Совет 12 основан на материалах сайта SGI STL , посвященных потоковой безопасности. Информация о подсчете ссылок в многопоточной среде из совета 13 почерпнута из статьи Саттера . Идея совета 15 была подсказана статьей Ривса «Using Standard string in the Real World, Part 2» . Методика непосредственной записи данных в vector , продемонстрированная в совете 16, была предложена Марком Роджерсом. В совет 17 была включена информация из Usenet, авторы - Симел Наран (Siemel Naran) и Карл Баррон (Carl Barron). Совет 18 был позаимствован из статьи Саттера «When Is a Container Not a Container?» . Для совета 20 Марк Роджерс предложил идею преобразования указателя в объект посредством разыменования, а Скотт Левандовски (Scott Lewandowski) разработал представленную версию DereferenceLess. Совет 21 основан на сообщении Дуга Харрисона (Doug Harrison) в конференцию microsoft.public.vc.stl, но решение о том, чтобы ограничить рамки этого совета проблемой равенства, принял я сам. Совет 22 основан на статье Саттера «Standard Library News: sets and maps» . Совет 23 был подсказан статьей Остерна «Why You Shouldn"t Use set - and What to Use Instead» ; Дэвид Смоллберг (David Smallberg) усовершенствовал мою реализацию DataCompare. Описание хэшированных контейнеров Dinkumware основано на статье Плаугера (Plauger) «Hash Tables» . Марк Роджерс не согласен с выводами совета 26, но первоначально этот совет был подсказан его замечанием относительно того, что некоторые функции контейнеров принимают только аргументы типа iterator. Работа над советом 29 вдохновлялась дискуссиями в Usenet с участием Мэтта Остерна и Джеймса Канце (James Kanze); на меня также повлияла статья Клауса Крефта (Klaus Kreft) и Анжелики Лангер (Angelika Langer) «А Sophisticated Implementation of User-Defined Inserters and Extractors» . Совет 30 основан на материалак раздела 5.4.2 книги Джосаттиса «The C++ Standard Library» . В совете 31 Марко Далла Гасперина (Marco Dalla Gasperina) предложил пример использования nth_element для вычисления медианы, а использование этого алгоритма для поиска процентилей взято прямо из раздела 18.7.1 книги Страуструпа (Stroustrup) «The C++ Programming Language». Совет 32 был вдохновлен материалами раздела 5.6.1 книги Джосаттиса «The C++ Standard Library». Совет 35 появился под влиянием статьи Остерна «How to Do Case-Insensitive String Comparison» , а сообщения Джеймса Канце и Джона Поттера (John Potter) помогли мне лучше разобраться в сути происходящего. Реализация copy_if, приведенная в совете 36, позаимствована из раздела 18.6.1 книги Страуструпа «The C++ Programming Language» . В основу совета 39 заложены публикации Джосаттиса, который впервые упомянул о «предикатах с состоянием» в своей книге «The C++ Standard Library» и в статье «Predicates vs. Function Objects» . В своей книге я использую его пример и рекомевдую предложенное им решение, хотя термин «чистая функция» принадлежит мне. В совете 41 Мeтт Остерн подтвердил мои подозрения относительно происхождения имен mem_fun и mem_fun_ref. Совет 42 берет свое начало из лекции, прочитанной мне Марком Роджерсом, когда я нарушил рекомендацию этого совета. Марку Роджерсу также принадлежит приведенное в совете 44 замечание о том, что при внешнем поиске в контейнерах map и multimap анализируются оба компонента пары, тогда как при поиске функциями контейнера учитывается только первый компонент (ключ). В совете 45 использована информация от разных участников clem, среди которых Джон Поттер, Марсин Касперски (Marcin Kasperski), Pete Becker (Пит Бекер), Деннис Йель (Dennis Yelle) и Дэвид Абрахаме (David Abrahams). Дэвид Смоллберг подсказал мне идею применения equal_range для поиска на базе эквивалентности и подсчета результатов в сортированных последовательных контейнерах. Андрей Александреску (Andrei Alexandrescu) помог разобраться в условиях возникновения проблемы «ссылки на ссылку», упоминаемой в совете 50; приведенный в книге пример основан на аналогичном примере Марка Роджерса, взятом с сайта Boost .

Скотт Мэйерс

Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Отзывы о третьей редакции Эффективного использования C++

Книга Скотта Мейерса «Эффективное использование C++», третья редакция – это концентрация опыта программирования – того опыта, который без нее достался бы вам дорогой ценой. Эта книга – великолепный источник, который я рекомендую всем, кто пишет на C++ профессионально.

Питер Дулимов, ME, инженер, подразделение оценки и исследований NAVSYSCOM, Австралия

Третья редакция остается лучшей книгой, посвященной тому, как сложить вместе все части C++ для создания эффективных и внутренне целостных программ. Если вы претендуете на то, чтобы быть программистом C++, то должны ее прочитать.

Эрик Наглер, консультант, преподаватель и автор «Изучая C++»

Первая редакция этой книги была одной из небольшого (весьма небольшого) числа книг, благодаря которым я ощутимо повысил свой уровень как профессионального разработчика программного обеспечения. Как и другие книги из этого ряда, она оказалась практичной и легкой для чтения, но при этом содержала множество важных советов. «Эффективное использование C++», третья редакция, продолжает эту традицию. C++ – очень мощный язык программирования. Если C дает веревку, по которой можно забраться на вершину горы, то C++ – это целый магазин, в котором самые разные люди готовы помочь вам завязать на этой веревке узлы. Овладение материалом, приведенным в этой книге, определенно повысит вашу способность эффективно использовать C++ и не умереть при этом от напряжения.

Джек В. Ривес, исполнительный директор Bleading Edge Software Technologies

Каждый новый разработчик, который приходит в мою команду, сразу получает задание – прочесть эту книгу.

Майкл Ланцетта, ведущий инженер по программному обеспечению

Я прочитал первую редакцию «Эффективного использования C++» около 9 лет назад, и эта книга сразу стала одной из моих любимых книг по C++. На мой взгляд, третье издание «Эффективного использования C++» остается обязательным к прочтению для всех, кто желает эффективно программировать на C++. Мы будем жить в лучшем мире, если программисты C++ прочтут эту книгу прежде, чем написать первую строку профессионального кода.

Дэнни Раббани, инженер по программному обеспечению

Первое издание «Эффективного использования C++» Скотта Мейерса попалось мне, когда я был рядовым программистом и напряженно старался как можно лучше выполнить порученную работу. И это было спасением! Я обнаружил, что советы Мейерса практически полезны и эффективны, что они на 100 % реализуют то, что обещают. Третья редакция помогает в практическом применении C++ при работе над современными серьезными программными проектами, предоставляя информацию о самых новых средствах и возможностях языка. Я с удовольствием обнаружил, что могу найти много нового и интересного для себя в третьем издании книги, которую, как мне казалось, знаю очень хорошо.

Майкл Топик, технический программный менеджер

Это авторитетное руководство от Скотта Мейерса, гуру C++, предназначенное для каждого, кто хочет применять C++ безопасно и эффективно, или же переходит к C++ от любого другого объектно-ориентированного языка. Эта книга содержит ценную информацию, изложенную в ясном, сжатом, занимательном и проницательном стиле.

Сиддхартха Каран Сингх, разработчик программного обеспечения

Благодарности

Книга «Эффективное использование C++» существует уже 15 лет, а изучать C++ я начал примерно за 5 лет до того, как написал ее. Таким образом, работа над этим проектом ведется около 20 лет. За это время я получал пожелания, замечания, исправления, а иногда и ошеломляющие наблюдения от сотен (тысяч?) людей. Каждый из них помог развитию «Эффективного использования C++». Я благодарен им всем.

Я давно уже отказался от попыток запомнить, где и чему я научился сам, но один источник не могу не упомянуть, поскольку пользуюсь им постоянно. Это группы новостей Usenet, в особенности comp.lang.c++.moderated и comp.std.c++. Многие правила, приведенные в этой книге (возможно, большинство), появились как результат осмысления технических идей, обсуждавшихся в этих группах.

В отборе нового материала, вошедшего в третье издание книги, мне помогал Стив Дьюхэрст (Steve Dewhurst). В правиле 11 идея реализации оператора operator= путем копирования и обмена почерпнута из заметок Герба Саттера (Herb Sutter), а именно из задачи 13 его книги «Exceptional C++» (Addison-Wesley, 2000). Идея о захвате ресурса как инициализации (правило 13) заимствована из книги «Язык программирования C++» («The C++ Programming Language», Addison– Wesley, 2002) Бьярна Страуструпа. Идея правила 17 взята из раздела «Передовые методы» («Best practices») на сайте «Boost shared_ptr» (http:// boost.org/libs/ smart_ptr/shared_ptr.htm#BestPractices) и уточнена на основе материала задачи 21 из книги Herb Sutter «More exceptional C++» (Addison-Wesley, 2002). На правило 29 меня вдохновило развернутое исследование этой темы, предпринятое Гербом Саттером, в задачах 8-19 из книги «Exceptional C++», а также в задачах 17–23 из «More exceptional C++» и задачах 11–13 из его же книги Exceptional C++ Style» (Addison-Wesley, 2005). Дэвид Абрахамс (David Abrahams) помог мне лучше понять три принципа гарантирования безопасности исключений. Идиома невиртуального интерфейса (NVI) в правиле 35 взята из колонки Герба Саттера «Виртуальность» (Virtuality) в сентябрьском номере 2001 г. журнала «C/C++ Users Journal». Упомянутые в том же правиле паттерны проектирования «Шаблонный метод» (Template Method) и «Стратегия» взяты из книги «Design Patterns» (Addison-Wesley, 1995) Эриха Гамма (Erich Gamma), Ричарда Хелма (Richard Helm), Ральфа Джонсона (Ralf Johnson) и Джона Влиссидеса (John Vlissides). Идею применения идиомы NVI в правиле 37 подсказал Хендрик Шобер (Hendrik Schober). Вклад Дэвида Смаллберга (David Smallberg) – реализация множества, описанная в правиле 38. Сделанное в правиле 39 наблюдение о том, что оптимизация пустого базового класса в принципе невозможна при множественном наследовании, заимствовано из книги Дэвида Вандевурде (David Vandevoorde) и Николая М. Джоссутиса (Nickolai M. Josuttis) «Templates C++» («Шаблоны в языке C++») (Addison-Wesley, 2003). Изложенное в правиле 42 мое первоначальное представление о том, для чего нужно ключевое слово typename, основано на документе «Часто задаваемые вопросы о C++ и C» («C++ and C FAQ») (http://www.comeaucomputing.com/techtalk/#typename), который поддерживает Грег Комо (Greg Comeau), а Леор Золман (Leor Zolman) помог мне осознать, что это представление ошибочно (моя вина, а не Грега). Тема правила 46 возникла из речи Дэна Сакса (Dan Saks) «Как заводить новых друзей». Высказанная в конце правила 52 идея о том, что если вы объявляете одну версию оператора new, то должны объявлять и все остальные, изложена в задаче 22 книги «Exceptional C++» Герба Саттера. Мое понимание процесса рецензирования Boost (суммированное в правиле 55) было уточнено Дэвидом Абрахамсом.

Все вышесказанное касается того, где и от кого чему-то научился именно я, независимо от того, кто первым опубликовал материал на соответствующую тему.

В моих заметках также сказано, что я использовал информацию, полученную от Стива Клемеджа (Steve Clamage), Антона Тракса (Antoine Trux), Тимоти Кнокса (Timothy Knox) и Майка Коэлблинга (Mike Kaelbling), хотя, к сожалению, не уточняется – где и как.

Черновики первого издания просматривали Том Карджилл (Tom Cargill), Гленн Каролл (Glenn Caroll), Тони Дэвис (Tony Davis), Брайн Керниган (Brian Kernigan), Жак Кирман (Jak Kirman), Дуг Ли (Doug Lea), Моисей Лежтер (Moises Lejter), Юджин Сантос мл. (Eugene Santos, Jr), Джон Шевчук (John Shewchuk), Джон Стаско (John Stasko), Бьерн Страуструп (Bjarne Stroustrup), Барбара Тилли (Barbara Tilly) и Нэнси Л. Урбано (Nancy L. Urbano). Кроме того, пожелания относительно улучшений, которые были включены в более поздние переиздания, высказывали Нэнси Л. Урбано, Крис Трейчел (Chris Treichel), Дэвид Корбин (David Corbin), Пол Гибсон (Paul Gibson), Стив Виноски (Steve Vinoski), Том Карджилл (Tom Cargill), Нейл Родес (Neil Rhodes), Дэвид Берн (David Bern), Расс Вильямс (Russ Williams), Роберт Бразил (Robert Brazile), Дуг Морган (Doug Morgan), Уве Штейнмюллер (Uwe Steinmuller), Марк Сомер (Mark Somer), Дуг Мур (Doug Moore), Дэвид Смаллберг, Сейт Мельтцер (Seith Meltzer), Олег Штейнбук (Oleg Steinbuk), Давид Папурт (David Papurt), Тони Хэнсен (Tony Hansen), Питер Мак-Клуски (Peter McCluskey), Стефан Кухлинс (Stefan Kuhlins), Дэвид Браунегг (David Braunegg), Поль Чисхолм (Paul Chisholm), Адам Зелл (Adam Zell), Кловис Тондо, Майк Коэлблинг, Натраж Кини (Natraj Kini), Ларс Ньюман (Lars Numan), Грег Лутц (Greg Lutz), Тим Джонсон, Джон Лакос (John Lakos), Роджер Скотт (Roger Scott), Скотт Фроман (Scott Frohman), Алан Рукс (Alan Rooks), Роберт Пур (Robert Poor), Эрик Наглер (Eric Nagler), Антон Тракс, Кад Роукс (Cade Roux), Чандрика Гокул (Chandrika Gokul), Рэнди Мангоба (Randy Mangoba) и Гленн Тейтельбаум (Glenn Teitelbaum).

Черновики второго издания проверяли: Дерек Босх (Derek Bosch), Тим Джонсон (Tim Johnson), Брайн Керниган, Юничи Кимура (Junichi Kimura), Скотт Левандовски (Scott Lewandowski), Лаура Михаелс (Laura Michaels), Дэвид Смаллберг (David Smallberg), Кловис Тонадо (Clovis Tonado), Крис Ван Вик (Chris Van Wyk) и Олег Заблуда (Oleg Zabluda). Более поздние тиражи выиграли от комментариев Дэниела Штейнберга (Daniel Steinberg), Арунпрасад Марате (Arunprasad Marathe), Дуга Стаппа (Doug Stapp), Роберта Халла (Robert Hall), Черилла Фергюссона (Cheryl Ferguson), Гари Бартлетта (Gary Bartlett), Майкла Тамма (Michael Tamm), Кендалла Бимана (Kendall Beaman), Эрика Наглера, Макса Хайлперина (Max Hailperin), Джо Готтмана (Joe Gottman), Ричарда Викса (Richard Weeks), Валентина Боннарда (Valentin Bonnard), Юн Хи (Jun He), Тима Кинга (Tim King), Дона Майлера (Don Mailer), Теда Хилла (Ted Hill), Марка Харрисона (Marc Harrison), Майкла Рубинштейна (Michael Rubinstein), Марка Роджерса (Marc Rodgers), Дэвида Го (David Goh), Брентона Купера (Brenton Cooper), Энди Томаса-Крамера (Andy Thomas-Cramer), Антона Тракса, Джона Вальта (John Walt), Брайана Шарона (Brian Sharon), Лиам Фитцпатрик (Liam Fitzpatric), Бернда Мора (Bernd Mohr), Гарри Йи (Gary Yee), Джона О"Ханли (John O"Hanley), Бреди Патресона (Brady Paterson), Кристофера Петерсона (Christopher Peterson), Феликса Клузняка (Feliks Kluzniak, Изи Даниетц (Isi Dunetz), Кристофера Креутци (Christopher Creutzi), Яна Купера (Ian Cooper), Карла Харриса (Carl Harris), Марка Стикеля (Marc Stickel), Клея Будина (Clay Budin), Панайотиса Мацинопулоса (Panayotis Matsinopulos), Дэвида Смаллберга, Херба Саттера, Пажо Мисленцевича (Pajo Misljencevic), Джулио Агостини (Giulio Agostini), Фредерика Бломквиста (Fredrik Blonqvist), Джимми Снайдера (Jimmy Snyder), Бириал Дженсен (Byrial Jensen), Витольда Кузьминского (Witold Kuzminski), Казунобу Курияма (Kazunobu Kuriyama), Майкла Кристенсена (Michael Christensen), Йорга Янеза Теруела (Jorge Yanez Teruel), Марка Дэвиса (Mark Davis), Марти Рабиновича (Marty Rabinowitz), Арес Лага (Ares Lagae) и Александра Медведева.



Рекомендуем почитать

Наверх