4. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 4
С++17
С++11/14
С++98
С
High level
Expert level
Modern C++
5. “Within C++ is a smaller, simpler, safer
language struggling to get out”
Bjarne Stroustrup
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 5
6. High level:
Парадигма RAII и исключения (exceptions)
Алгоритмы и контейнеры STL и boost
Семантика перемещения
λ-функции
Классы и конструкторы
Простые шаблоны
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 6
Expert level:
Операторы new/delete, владеющие
указатели
Пользовательские операции копирования
и перемещения
Пользовательские деструкторы
Закрытое, защищённое, ромбовидное,
виртуальное наследование
Шаблонная магия, SFINAE
Все функции языка Си, препроцессор
«Голые» циклы
Классический слайд с телом и заголовком
17. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 17
// ...
std::sort(oldCookies.begin(), oldCookies.end(), compareCookies);
std::sort(newCookies.begin(), newCookies.end(), compareCookies);
auto oldIt = oldCookies.begin();
auto newIt = newCookies.begin();
std::vector<std::pair<Cookie, int>> diff;
while ((oldIt != oldCookies.end()) || (newIt != newCookies.end()))
{
if (compareCookies(*oldIt, *newIt))
{
diff.push_back(std::pair<Cookie, int>(*oldIt, 1));
++oldIt;
}
else if (compareCookies(*newIt, *oldIt))
{
// ...
18. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 18
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
};
bool compareCookies(const Cookie& a, const Cookie& b)
{
if (a.tastiness != b.tastiness)
return a.tastiness < b.tastiness;
if (a.weight != b.weight)
return a.weight < b.weight;
if (a.volume != b.volume)
return a.volume < b.volume;
return a.name < b.name;
}
19. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 19
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
};
bool operator<(const Cookie& a, const Cookie& b)
{
if (a.tastiness != b.tastiness)
return a.tastiness < b.tastiness;
if (a.weight != b.weight)
return a.weight < b.weight;
if (a.volume != b.volume)
return a.volume < b.volume;
return a.name < b.name;
}
20. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 20
// ...
std::sort(oldCookies.begin(), oldCookies.end(), compareCookies);
std::sort(newCookies.begin(), newCookies.end(), compareCookies);
auto oldIt = oldCookies.begin();
auto newIt = newCookies.begin();
while ((oldIt != oldCookies.end()) || (newIt != newCookies.end()))
{
if (compareCookies(*oldIt, *newIt))
{
diff.push_back(std::pair<Cookie, int>(*oldIt, 1));
++oldIt;
}
else if (compareCookies(*newIt, *oldIt))
{
// ...
21. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 21
// ...
std::sort(oldCookies.begin(), oldCookies.end());
std::sort(newCookies.begin(), newCookies.end());
auto oldIt = oldCookies.begin();
auto newIt = newCookies.begin();
while ((oldIt != oldCookies.end()) || (newIt != newCookies.end()))
{
if (*oldIt < *newIt)
{
diff.push_back(std::pair<Cookie, int>(*oldIt, 1));
++oldIt;
}
else if (*newIt < *oldIt)
{
// ...
22. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 22
bool operator<(const Cookie& a, const Cookie& b)
{
if (a.tastiness != b.tastiness)
return a.tastiness < b.tastiness;
if (a.weight != b.weight)
return a.weight < b.weight;
if (a.volume != b.volume)
return a.volume < b.volume;
return a.name < b.name;
}
23. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 23
bool operator<(const Cookie& a, const Cookie& b)
{
return std::make_tuple(a.tastiness, a.weight, a.volume, a.name) <
std::make_tuple(b.tastiness, b.weight, b.volume, b.name);
}
24. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 24
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
};
bool operator<(const Cookie& a, const Cookie& b)
{
return std::make_tuple(a.tastiness, a.weight, a.volume, a.name) <
std::make_tuple(b.tastiness, b.weight, b.volume, b.name);
}
25. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 25
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
};
bool operator<(const Cookie& a, const Cookie& b)
{
return std::tie(a.tastiness, a.weight, a.volume, a.name) <
std::tie(b.tastiness, b.weight, b.volume, b.name);
}
26. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 26
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
auto rank() const
{
return std::tie(tastiness, weight, volume, name);
}
};
bool operator<(const Cookie& a, const Cookie& b)
{
return a.rank() < b.rank();
}
Implementing comparison operators via 'tuple' and 'tie', a good idea?
27. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 27
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
auto rank() const // -> std::tuple<const int&, const double&,
{ // const double&, const std::string&>
return std::tie(tastiness, weight, volume, name);
}
};
bool operator<(const Cookie& a, const Cookie& b)
{
return a.rank() < b.rank();
}
Implementing comparison operators via 'tuple' and 'tie', a good idea?
28. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 28
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
auto rank() const // -> std::tuple<const int&, const double&,
{ // const double&, const std::string&>
return std::tie(tastiness, weight, volume, name);
}
};
bool operator<(const Cookie& a, const Cookie& b)
{
return a.rank() < b.rank();
}
bool operator==(const Cookie& a, const Cookie& b)
{
return a.rank() == b.rank();
}
45. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 45
std::vector<Cookie> lostCookies;
std::set_difference(oldCookies.begin(), oldCookies.end(),
newCookies.begin(), newCookies.end(),
std::back_inserter(lostCookies));
std::vector<Cookie> appearedCookies;
std::set_difference(newCookies.begin(), newCookies.end(),
oldCookies.begin(), oldCookies.end(),
std::back_inserter(appearedCookies));
46. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 46
std::vector<Cookie> lostCookies;
boost::range::set_difference(oldCookies, newCookies, std::back_inserter(lostCookies));
std::vector<Cookie> appearedCookies;
boost::range::set_difference(newCookies, oldCookies, std::back_inserter(appearedCookies));
47. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 47
std::vector<Cookie> lostCookies;
boost::range::set_difference(oldCookies, newCookies, std::back_inserter(lostCookies));
std::vector<Cookie> appearedCookies;
boost::range::set_difference(newCookies, oldCookies, std::back_inserter(appearedCookies));
for (const auto& cookie : lostCookies)
{
std::cout << "We've lost a friend: " << cookie << "." << std::endl;
}
for (const auto& cookie : appearedCookies)
{
std::cout << "We got a new friend: " << cookie << "." << std::endl;
}
48. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 48
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies = collectSortedCookies(oldBakery);
std::vector<Cookie> newCookies = collectSortedCookies(newBakery);
return lostCookies.empty() && appearedCookies.empty();
}
std::vector<Cookie> lostCookies;
boost::range::set_difference(oldCookies, newCookies, std::back_inserter(lostCookies));
std::vector<Cookie> appearedCookies;
boost::range::set_difference(newCookies, oldCookies, std::back_inserter(appearedCookies));
for (const auto& cookie : lostCookies)
{
std::cout << "We've lost a friend: " << cookie << "." << std::endl;
}
for (const auto& cookie : appearedCookies)
{
std::cout << "We got a new friend: " << cookie << "." << std::endl;
}
49. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 49
bool doCompare(const Bakery* oldBakery, const Bakery* newBakery)
{
if (!oldBakery || !newBakery)
{
return false;
}
std::vector<Cookie> oldCookies;
std::vector<Cookie> newCookies;
collectCookies(*oldBakery, oldCookies);
collectCookies(*newBakery, newCookies);
std::sort(oldCookies.begin(), oldCookies.end(), compareCookies);
std::sort(newCookies.begin(), newCookies.end(), compareCookies);
auto oldIt = oldCookies.begin();
auto newIt = newCookies.begin();
std::vector<std::pair<Cookie, int>> diff;
while ((oldIt != oldCookies.end()) || (newIt != newCookies.end()))
{
if (compareCookies(*oldIt, *newIt))
{
diff.push_back(std::pair<Cookie, int>(*oldIt, 1));
++oldIt;
}
else if (compareCookies(*newIt, *oldIt))
{
diff.push_back(std::pair<Cookie, int>(*newIt, 2));
++newIt;
}
else
{
++oldIt;
++newIt;
}
}
if (oldIt != oldCookies.end())
{
for (; oldIt < oldCookies.end(); oldIt++)
{
diff.push_back(std::pair<Cookie, int>(*oldIt, 1));
}
}
if (newIt != newCookies.end())
{
for (; newIt < newCookies.end(); newIt++)
{
diff.push_back(std::pair<Cookie, int>(*newIt, 2));
}
}
for (const auto& item : diff)
{
std::cout << "Bakery #" << item.second <<
" has a different cookie "" << item.first.name <<
"" (weight=" << item.first.weight <<
", volume=" << item.first.volume <<
", tastiness=" << item.first.tastiness << ")." << std::endl;
}
if (diff.size() > 0)
return false;
return true;
}
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies = collectSortedCookies(oldBakery);
std::vector<Cookie> newCookies = collectSortedCookies(newBakery);
std::vector<Cookie> lostCookies;
boost::range::set_difference(oldCookies, newCookies, std::back_inserter(lostCookies));
std::vector<Cookie> appearedCookies;
boost::range::set_difference(newCookies, oldCookies, std::back_inserter(appearedCookies));
for (const auto& cookie : lostCookies)
{
std::cout << "We've lost a friend: " << cookie << "." << std::endl;
}
for (const auto& cookie : appearedCookies)
{
std::cout << "We got a new friend: " << cookie << "." << std::endl;
}
return lostCookies.empty() && appearedCookies.empty();
}
50. Код, использующий стандартные алгоритмы и контейнеры:
Короче
Проще
Надёжнее
Обычно эффективнее
Проще переиспользовать
Не привязан к конкретной платформе
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 50
51. И ещё немного
по теме…
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 51
52. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 52
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
auto rank() const
{
return std::tie(tastiness, weight, volume, name);
}
};
53. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 53
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
double density() const
{
return weight / volume;
}
auto rank() const
{
return std::tie(tastiness, density(), name);
}
};
54. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 54
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
double density() const
{
return weight / volume;
}
auto rank() const
{
return std::make_tuple(tastiness, density(), std::ref(name));
}
};
55. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 55
struct Cookie
{
double weight;
double volume;
std::string name;
int tastiness;
double density() const
{
return weight / volume;
}
auto rank() const // -> std::tuple<int, double, const std::string&>
{
return std::make_tuple(tastiness, density(), std::ref(name));
}
};
56. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 56
std::vector<Cookie> lostCookies;
boost::range::set_difference(oldCookies, newCookies, std::back_inserter(lostCookies));
std::vector<Cookie> appearedCookies;
boost::range::set_difference(newCookies, oldCookies, std::back_inserter(appearedCookies));
for (const auto& cookie : lostCookies)
{
std::cout << "We've lost a friend: " << cookie << "." << std::endl;
}
for (const auto& cookie : appearedCookies)
{
std::cout << "We got a new friend: " << cookie << "." << std::endl;
}
57. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 57
auto reportLost = [](const Cookie& cookie)
{
std::cout << "We've lost a friend: " << cookie << "." << std::endl;
};
auto reportAppeared = [](const Cookie& cookie)
{
std::cout << "We got a new friend: " << cookie << "." << std::endl;
};
boost::range::set_difference(oldCookies, newCookies,
boost::make_function_output_iterator(reportLost));
boost::range::set_difference(newCookies, oldCookies,
boost::make_function_output_iterator(reportAppeared));
58. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 58
auto reportLost = [](const Cookie& cookie)
{
std::cout << "We've lost a friend: " << cookie << "." << std::endl;
};
auto reportAppeared = [](const Cookie& cookie)
{
std::cout << "We got a new friend: " << cookie << "." << std::endl;
};
boost::range::set_difference(oldCookies, newCookies,
boost::make_function_output_iterator(reportLost));
boost::range::set_difference(newCookies, oldCookies,
boost::make_function_output_iterator(reportAppeared));
return oldCookies == newCookies; // was "lostCookies.empty() && appearedCookies.empty()"
59. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 59
auto f = [](int x) { std::cout << x; };
auto it = boost::make_function_output_iterator(f);
decltype(it) it2 = it; // Ok, copied
it2 = it; // Does not compile, cannot assign!
std::function<void(int)> f = [](int x) { std::cout << x; };
auto it = boost::make_function_output_iterator(f);
decltype(it) it2 = it; // Ok, copied
it2 = it; // Ok, iterator holds an std::function instance
auto f = [](int x) { std::cout << x; };
auto it = boost::make_function_output_iterator(std::ref(f));
decltype(it) it2 = it; // Ok, copied
it2 = it; // Ok, iterator holds a reference
auto f = +[](int x) { std::cout << x; };
auto it = boost::make_function_output_iterator(f);
decltype(it) it2 = it; // Ok, copied
it2 = it; // Ok, note the plus before [], it triggers special C++ magic
Resolving ambiguous overload
on function pointer and
std::function for a lambda using +
boost::function_output_iterator
constructed from lambda
function is not assignable
60. Советы:
Мыслите на высоком уровне
Код должен ясно выражать намерение
Знайте свои инструменты и используйте их к месту
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 60
// references
// auto variables
// auto function return type
// move semantics
// lambda functions
operator<();
std::make_tuple();
std::tie();
std::ref();
std::make_pair();
std::vector<T>::emplace_back();
std::vector<T>::empty();
operator<<();
std::back_inserter();
std::set_difference();
boost::range::set_difference();
boost::make_function_output_iterator();
std::function<>
61. Мыслите на высоком уровне
Код должен ясно выражать намерение
Знайте свои инструменты и используйте их к месту
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016
std::make_pair();
std::vector<T>::emplace_back();
std::vector<T>::empty();
operator<<();
std::back_inserter();
std::set_difference();
boost::range::set_difference();
boost::make_function_output_iterator();
std::function<>
Спасибо за внимание!
// references
// auto variables
// auto function return type
// move semantics
// lambda functions
operator<();
std::make_tuple();
std::tie();
std::ref();
61
62. "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 62
compareRanked(Cookie const&, Cookie const&):
movl 16(%rsi), %edx
cmpl %edx, 16(%rdi)
movl $1, %eax
jl .L2
movl $0, %eax
jg .L2
movsd (%rdi), %xmm1
movl $1, %eax
movsd (%rsi), %xmm0
ucomisd %xmm1, %xmm0
ja .L2
xorl %eax, %eax
ucomisd %xmm0, %xmm1
ja .L2
movsd 8(%rsi), %xmm0
ucomisd 8(%rdi), %xmm0
seta %al
.L2:
rep ret
compareDirect(Cookie const&, Cookie const&):
movl 16(%rsi), %eax
cmpl %eax, 16(%rdi)
je .L9
setl %al
ret
.L9:
movsd (%rdi), %xmm0
movsd (%rsi), %xmm1
ucomisd %xmm1, %xmm0
jp .L13
jne .L13
movsd 8(%rsi), %xmm0
ucomisd 8(%rdi), %xmm0
seta %al
ret
.L13:
ucomisd %xmm0, %xmm1
seta %al
ret
63. Ссылки
Which boost features overlap with C++11?
In-depth: Functional programming in C++
Implementing comparison operators via 'tuple' and 'tie', a good idea?
Sean Parent: C++ Seasoning (no raw loops)
boost::function_output_iterator constructed from lambda function is not assignable
Resolving ambiguous overload on function pointer and std::function for a lambda using +
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 63
Notas do Editor
Разрабатываю на С++ больше 10 лет, занимался обработкой изображений и 3D графикой. В настоящий момент технический менеджер в компании Align Technology. Это международная компания, производитель медицинского оборудования для нужд ортодонтии. Использует ПО, разработанную на С++. Я занимаюсь поддержкой и развитием 3D CAD приложения для планирования ортодонтического лечения. Также улучшаю качество кода и процесс разработки.
Образ С++
По поводу фичей, взятых из boost: http://stackoverflow.com/q/8851670/261217
Исходный текст функции.
Цель
Посмотрим на указатели
Используем ссылки
Посмотрим на вызов collectCookies
Убедимся, что он инициализирует вектор печенек
Будем передавать его по значению
Посмотрим на функцию compareCookies
Посмотрим на прототип функции compareCookies
Используем operator<. Никто не будет сравнивать значения с помощью вызова функции. Ну, если вы не пишете на Java.
Вернёмся к коду основной функции
Используем прямое сравнение
Вернёмся к оператору сравнения
Используем std::tuple для лексикографического сравнения
Посмотрим ещё раз на сам тип Cookie
Используем std::tie, чтобы не копировать std::string лишний раз
Кроме того, посмотрим по дублирование списка членов
Используем функцию rank(), чтобы не дублировать список членов
Добавим комментарий с явным типом возвращаемого значения
Объявим operator== по аналогии
Посмотрим на два вызова std::sort
Посмотрим на два вызова std::sort
Устраним дублирование: уберём std::sort в collectCookies. Для наглядности переименуем его в collectSortedCookies.
Посмотрим на явный конструктор std::pair
Используем std::make_pair
Используем uniform initialization
Используем emplace_back()
Посмотрим на цикл вывода
Добавим оператора вывода в поток для печеньки
Посмотрим на конструкцию вывода
Заменим на эквивалентный вызов empty()
Начинаем догадываться, что всё это значит. По этому поводу пишем юнит-тесты. Находим баг.
Используем готовый алгоритм. Потом обращаем внимание, что неудобно писать много раз begin()/end().
Используем аналог из boost::range
Добавляем код с выводом. Свой вывод для пропавших и появившихся печенек.
Добавляем код с выводом. Свой вывод для пропавших и появившихся печенек. Понимаем, что вывод – это всё, что нам нужно.
Цель достигнута!
Сейчас ещё чуть-чуть кода и котик, чтобы вы не расстраивались.
Небольшой бонус относительно std::tie и std::make_tuple. Вспомним определение метода rank().
Что если в вычислении ранка будут участвовать не только поля класса? Код просто не скомпилируется.
Придётся вернуться к make_tuple(), но чтобы не копировать строку каждый раз, обернём её в std::reference_wrapper.
Явно укажем выведенный тип.
Взглянем ещё раз на тело функции. Осознаем, что у нас нет нужды сохранять множества в явном виде.
Заиспользуем boost::function_output_iterator.
Поскольку контейнеров в явном виде больше нет, придётся изменить способ вычисления возвращаемого значения.
Есть ли проблемы с boost::make_function_output_iterator? Код может не скомпилироваться, т.к. итератор хранит предикат по значению, а у замыкания удалён оператор присваивания. Следовательно, получившийся итератор нельзя присваивать, и формально он не удовлетворяет концепту Iterator.
Есть разные способы исправить итератор.
Ассемблерный код для двух функций сравнения: с использованием метода rank() – compareRanked, и с использованием цепочки if’ов – compareDirect. Здесь из класса Cookie было исключено строковое поле name, чтобы не загромождать вывод. Скомпилировано x86 gcc 5.3.0 -std=c++14 -O2