Повседневный С++: boost и STL
Михаил Матросов
mikhail.matrosov@gmail.com
mmatrosov@aligntech.com
1
2
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 3
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 4
С++17
С++11/14
С++98
С
High level
Expert level
Modern C++
“Within C++ is a smaller, simpler, safer
language struggling to get out”
Bjarne Stroustrup
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 5
High level:
 Парадигма RAII и исключения (exceptions)
 Алгоритмы и контейнеры STL и boost
 Семантика перемещения
 λ-функции
 Классы и конструкторы
 Простые шаблоны
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 6
Expert level:
 Операторы new/delete, владеющие
указатели
 Пользовательские операции копирования
и перемещения
 Пользовательские деструкторы
 Закрытое, защищённое, ромбовидное,
виртуальное наследование
 Шаблонная магия, SFINAE
 Все функции языка Си, препроцессор
 «Голые» циклы
Классический слайд с телом и заголовком
7
Which boost
features overlap
with C++11?
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 8
bool doCompare(const Bakery* oldBakery, const Bakery* newBakery)
{
if (!oldBakery || !newBakery)
{
return false;
}
std::vector<Cookie> oldCookies;
std::vector<Cookie> newCookies;
std::vector<std::pair<Cookie, int>> diff;
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();
while ((oldIt != oldCookies.end()) || (newIt != newCookies.end()))
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 9
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();
}
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 10
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);
// ...
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 11
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies;
std::vector<Cookie> newCookies;
collectCookies(oldBakery, oldCookies);
collectCookies(newBakery, newCookies);
// ...
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 12
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies;
std::vector<Cookie> newCookies;
collectCookies(oldBakery, oldCookies);
collectCookies(newBakery, newCookies);
// ...
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 13
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 14
void collectCookies(const Bakery& bakery, std::vector<Cookie>& o_cookies);
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies;
std::vector<Cookie> newCookies;
collectCookies(oldBakery, oldCookies);
collectCookies(newBakery, newCookies);
// ...
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 15
std::vector<Cookie> collectCookies(const Bakery& bakery);
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies = collectCookies(oldBakery);
std::vector<Cookie> newCookies = collectCookies(newBakery);
// ...
In-depth: Functional programming in C++
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 16
λλ
=:
"Повседневный С++: 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))
{
// ...
"Повседневный С++: 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;
}
"Повседневный С++: 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;
}
"Повседневный С++: 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))
{
// ...
"Повседневный С++: 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)
{
// ...
"Повседневный С++: 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;
}
"Повседневный С++: 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);
}
"Повседневный С++: 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);
}
"Повседневный С++: 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);
}
"Повседневный С++: 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?
"Повседневный С++: 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?
"Повседневный С++: 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();
}
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 29
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 30
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies = collectCookies(oldBakery);
std::vector<Cookie> newCookies = collectCookies(newBakery);
std::sort(oldCookies.begin(), oldCookies.end());
std::sort(newCookies.begin(), newCookies.end());
// ...
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 31
std::vector<Cookie> collectCookies(const Bakery& bakery)
{
std::vector<Cookie> cookies;
// ...
// ...
return cookies;
}
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies = collectCookies(oldBakery);
std::vector<Cookie> newCookies = collectCookies(newBakery);
std::sort(oldCookies.begin(), oldCookies.end());
std::sort(newCookies.begin(), newCookies.end());
// ...
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 32
std::vector<Cookie> collectSortedCookies(const Bakery& bakery)
{
std::vector<Cookie> cookies;
// ...
std::sort(cookies.begin(), cookies.end());
return cookies;
}
bool doCompare(const Bakery& oldBakery, const Bakery& newBakery)
{
std::vector<Cookie> oldCookies = collectSortedCookies(oldBakery);
std::vector<Cookie> newCookies = collectSortedCookies(newBakery);
// ...
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 33
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 34
std::vector<std::pair<Cookie, int>> diff;
// ...
if (*oldIt < *newIt)
{
diff.push_back(std::pair<Cookie, int>(*oldIt, 1));
++oldIt;
}
else if (*newIt < *oldIt)
{
diff.push_back(std::pair<Cookie, int>(*newIt, 2));
++newIt;
}
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 35
std::vector<std::pair<Cookie, int>> diff;
// ...
if (*oldIt < *newIt)
{
diff.push_back(std::make_pair(*oldIt, 1));
++oldIt;
}
else if (*newIt < *oldIt)
{
diff.push_back(std::make_pair(*newIt, 2));
++newIt;
}
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 36
std::vector<std::pair<Cookie, int>> diff;
// ...
if (*oldIt < *newIt)
{
diff.push_back({ *oldIt, 1 });
++oldIt;
}
else if (*newIt < *oldIt)
{
diff.push_back({ *newIt, 2 });
++newIt;
}
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 37
std::vector<std::pair<Cookie, int>> diff;
// ...
if (*oldIt < *newIt)
{
diff.emplace_back(*oldIt, 1);
++oldIt;
}
else if (*newIt < *oldIt)
{
diff.emplace_back(*newIt, 2);
++newIt;
}
for (const auto& item : diff) // item is std::pair<Cookie, int>
{
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;
}
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 38
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 39
for (const auto& item : diff) // item is std::pair<Cookie, int>
{
std::cout << "Bakery #" << item.second <<
" has a different cookie " << item.first << "." << std::endl;
}
std::ostream& operator<<(std::ostream& stream, const Cookie& cookie)
{
return stream << """ << cookie.name <<
"" (weight=" << cookie.weight <<
", volume=" << cookie.volume <<
", tastiness=" << cookie.tastiness << ")";
}
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 40
if (diff.size() > 0)
return false;
return true;
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 41
return diff.empty();
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 42
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 43
auto oldIt = oldCookies.begin();
auto newIt = newCookies.begin();
std::vector<std::pair<Cookie, int>> diff;
while ((oldIt != oldCookies.end()) || (newIt != newCookies.end()))
{
if (*oldIt < *newIt)
{
diff.emplace_back(*oldIt, 1);
++oldIt;
}
else if (*newIt < *oldIt)
{
diff.emplace_back(*newIt, 2);
++newIt;
}
else
{
++oldIt;
++newIt;
}
}
for (; oldIt != oldCookies.end(); oldIt++)
{
diff.emplace_back(*oldIt, 1);
}
for (; newIt != newCookies.end(); newIt++)
{
diff.emplace_back(*newIt, 2);
}
1 2 5 7 8 9
1 3 5 6 9
2 7 83 6
oldCookies
newCookies
diff
Ошибка. Должно быть &&
Sean Parent: C++ Seasoning (no raw loops)
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 44
"Повседневный С++: 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));
"Повседневный С++: 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));
"Повседневный С++: 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;
}
"Повседневный С++: 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;
}
"Повседневный С++: 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();
}
Код, использующий стандартные алгоритмы и контейнеры:
 Короче
 Проще
 Надёжнее
 Обычно эффективнее
 Проще переиспользовать
 Не привязан к конкретной платформе
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 50
И ещё немного
по теме…
"Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 51
"Повседневный С++: 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);
}
};
"Повседневный С++: 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);
}
};
"Повседневный С++: 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));
}
};
"Повседневный С++: 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));
}
};
"Повседневный С++: 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;
}
"Повседневный С++: 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));
"Повседневный С++: 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()"
"Повседневный С++: 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
Советы:
 Мыслите на высоком уровне
 Код должен ясно выражать намерение
 Знайте свои инструменты и используйте их к месту
"Повседневный С++: 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<>
 Мыслите на высоком уровне
 Код должен ясно выражать намерение
 Знайте свои инструменты и используйте их к месту
"Повседневный С++: 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
"Повседневный С++: 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
Ссылки
 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

Михаил Матросов, Повседневный С++: boost и STL

  • 1.
    Повседневный С++: boostи STL Михаил Матросов mikhail.matrosov@gmail.com mmatrosov@aligntech.com 1
  • 2.
  • 3.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 3
  • 4.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 4 С++17 С++11/14 С++98 С High level Expert level Modern C++
  • 5.
    “Within C++ isa 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  Все функции языка Си, препроцессор  «Голые» циклы Классический слайд с телом и заголовком
  • 7.
  • 8.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 8 bool doCompare(const Bakery* oldBakery, const Bakery* newBakery) { if (!oldBakery || !newBakery) { return false; } std::vector<Cookie> oldCookies; std::vector<Cookie> newCookies; std::vector<std::pair<Cookie, int>> diff; 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(); while ((oldIt != oldCookies.end()) || (newIt != newCookies.end()))
  • 9.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 9 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(); }
  • 10.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 10 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); // ...
  • 11.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 11 bool doCompare(const Bakery& oldBakery, const Bakery& newBakery) { std::vector<Cookie> oldCookies; std::vector<Cookie> newCookies; collectCookies(oldBakery, oldCookies); collectCookies(newBakery, newCookies); // ...
  • 12.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 12
  • 13.
    bool doCompare(const Bakery&oldBakery, const Bakery& newBakery) { std::vector<Cookie> oldCookies; std::vector<Cookie> newCookies; collectCookies(oldBakery, oldCookies); collectCookies(newBakery, newCookies); // ... "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 13
  • 14.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 14 void collectCookies(const Bakery& bakery, std::vector<Cookie>& o_cookies); bool doCompare(const Bakery& oldBakery, const Bakery& newBakery) { std::vector<Cookie> oldCookies; std::vector<Cookie> newCookies; collectCookies(oldBakery, oldCookies); collectCookies(newBakery, newCookies); // ...
  • 15.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 15 std::vector<Cookie> collectCookies(const Bakery& bakery); bool doCompare(const Bakery& oldBakery, const Bakery& newBakery) { std::vector<Cookie> oldCookies = collectCookies(oldBakery); std::vector<Cookie> newCookies = collectCookies(newBakery); // ... In-depth: Functional programming in C++
  • 16.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 16 λλ =:
  • 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(); }
  • 29.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 29
  • 30.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 30 bool doCompare(const Bakery& oldBakery, const Bakery& newBakery) { std::vector<Cookie> oldCookies = collectCookies(oldBakery); std::vector<Cookie> newCookies = collectCookies(newBakery); std::sort(oldCookies.begin(), oldCookies.end()); std::sort(newCookies.begin(), newCookies.end()); // ...
  • 31.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 31 std::vector<Cookie> collectCookies(const Bakery& bakery) { std::vector<Cookie> cookies; // ... // ... return cookies; } bool doCompare(const Bakery& oldBakery, const Bakery& newBakery) { std::vector<Cookie> oldCookies = collectCookies(oldBakery); std::vector<Cookie> newCookies = collectCookies(newBakery); std::sort(oldCookies.begin(), oldCookies.end()); std::sort(newCookies.begin(), newCookies.end()); // ...
  • 32.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 32 std::vector<Cookie> collectSortedCookies(const Bakery& bakery) { std::vector<Cookie> cookies; // ... std::sort(cookies.begin(), cookies.end()); return cookies; } bool doCompare(const Bakery& oldBakery, const Bakery& newBakery) { std::vector<Cookie> oldCookies = collectSortedCookies(oldBakery); std::vector<Cookie> newCookies = collectSortedCookies(newBakery); // ...
  • 33.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 33
  • 34.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 34 std::vector<std::pair<Cookie, int>> diff; // ... if (*oldIt < *newIt) { diff.push_back(std::pair<Cookie, int>(*oldIt, 1)); ++oldIt; } else if (*newIt < *oldIt) { diff.push_back(std::pair<Cookie, int>(*newIt, 2)); ++newIt; }
  • 35.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 35 std::vector<std::pair<Cookie, int>> diff; // ... if (*oldIt < *newIt) { diff.push_back(std::make_pair(*oldIt, 1)); ++oldIt; } else if (*newIt < *oldIt) { diff.push_back(std::make_pair(*newIt, 2)); ++newIt; }
  • 36.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 36 std::vector<std::pair<Cookie, int>> diff; // ... if (*oldIt < *newIt) { diff.push_back({ *oldIt, 1 }); ++oldIt; } else if (*newIt < *oldIt) { diff.push_back({ *newIt, 2 }); ++newIt; }
  • 37.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 37 std::vector<std::pair<Cookie, int>> diff; // ... if (*oldIt < *newIt) { diff.emplace_back(*oldIt, 1); ++oldIt; } else if (*newIt < *oldIt) { diff.emplace_back(*newIt, 2); ++newIt; }
  • 38.
    for (const auto&item : diff) // item is std::pair<Cookie, int> { 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; } "Повседневный С++: boost и STL", Михаил Матросов, C++ Russia 2016 38
  • 39.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 39 for (const auto& item : diff) // item is std::pair<Cookie, int> { std::cout << "Bakery #" << item.second << " has a different cookie " << item.first << "." << std::endl; } std::ostream& operator<<(std::ostream& stream, const Cookie& cookie) { return stream << """ << cookie.name << "" (weight=" << cookie.weight << ", volume=" << cookie.volume << ", tastiness=" << cookie.tastiness << ")"; }
  • 40.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 40 if (diff.size() > 0) return false; return true;
  • 41.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 41 return diff.empty();
  • 42.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 42
  • 43.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 43 auto oldIt = oldCookies.begin(); auto newIt = newCookies.begin(); std::vector<std::pair<Cookie, int>> diff; while ((oldIt != oldCookies.end()) || (newIt != newCookies.end())) { if (*oldIt < *newIt) { diff.emplace_back(*oldIt, 1); ++oldIt; } else if (*newIt < *oldIt) { diff.emplace_back(*newIt, 2); ++newIt; } else { ++oldIt; ++newIt; } } for (; oldIt != oldCookies.end(); oldIt++) { diff.emplace_back(*oldIt, 1); } for (; newIt != newCookies.end(); newIt++) { diff.emplace_back(*newIt, 2); } 1 2 5 7 8 9 1 3 5 6 9 2 7 83 6 oldCookies newCookies diff Ошибка. Должно быть && Sean Parent: C++ Seasoning (no raw loops)
  • 44.
    "Повседневный С++: boostи STL", Михаил Матросов, C++ Russia 2016 44
  • 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 boostfeatures 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

Editor's Notes

  • #3 Разрабатываю на С++ больше 10 лет, занимался обработкой изображений и 3D графикой. В настоящий момент технический менеджер в компании Align Technology. Это международная компания, производитель медицинского оборудования для нужд ортодонтии. Использует ПО, разработанную на С++. Я занимаюсь поддержкой и развитием 3D CAD приложения для планирования ортодонтического лечения. Также улучшаю качество кода и процесс разработки.
  • #4 Образ С++
  • #7 По поводу фичей, взятых из boost: http://stackoverflow.com/q/8851670/261217
  • #9 Исходный текст функции.
  • #10 Цель
  • #11 Посмотрим на указатели
  • #12 Используем ссылки
  • #14 Посмотрим на вызов collectCookies
  • #15 Убедимся, что он инициализирует вектор печенек
  • #16 Будем передавать его по значению
  • #18 Посмотрим на функцию compareCookies
  • #19 Посмотрим на прототип функции compareCookies
  • #20 Используем operator<. Никто не будет сравнивать значения с помощью вызова функции. Ну, если вы не пишете на Java.
  • #21 Вернёмся к коду основной функции
  • #22 Используем прямое сравнение
  • #23 Вернёмся к оператору сравнения
  • #24 Используем std::tuple для лексикографического сравнения
  • #25 Посмотрим ещё раз на сам тип Cookie
  • #26 Используем std::tie, чтобы не копировать std::string лишний раз Кроме того, посмотрим по дублирование списка членов
  • #27 Используем функцию rank(), чтобы не дублировать список членов
  • #28 Добавим комментарий с явным типом возвращаемого значения
  • #29 Объявим operator== по аналогии
  • #31 Посмотрим на два вызова std::sort
  • #32 Посмотрим на два вызова std::sort
  • #33 Устраним дублирование: уберём std::sort в collectCookies. Для наглядности переименуем его в collectSortedCookies.
  • #35 Посмотрим на явный конструктор std::pair
  • #36 Используем std::make_pair
  • #37 Используем uniform initialization
  • #38 Используем emplace_back()
  • #39 Посмотрим на цикл вывода
  • #40 Добавим оператора вывода в поток для печеньки
  • #41 Посмотрим на конструкцию вывода
  • #42 Заменим на эквивалентный вызов empty()
  • #44 Начинаем догадываться, что всё это значит. По этому поводу пишем юнит-тесты. Находим баг.
  • #46 Используем готовый алгоритм. Потом обращаем внимание, что неудобно писать много раз begin()/end().
  • #47 Используем аналог из boost::range
  • #48 Добавляем код с выводом. Свой вывод для пропавших и появившихся печенек.
  • #49 Добавляем код с выводом. Свой вывод для пропавших и появившихся печенек. Понимаем, что вывод – это всё, что нам нужно.
  • #50 Цель достигнута!
  • #52 Сейчас ещё чуть-чуть кода и котик, чтобы вы не расстраивались.
  • #53 Небольшой бонус относительно std::tie и std::make_tuple. Вспомним определение метода rank().
  • #54 Что если в вычислении ранка будут участвовать не только поля класса? Код просто не скомпилируется.
  • #55 Придётся вернуться к make_tuple(), но чтобы не копировать строку каждый раз, обернём её в std::reference_wrapper.
  • #56 Явно укажем выведенный тип.
  • #57 Взглянем ещё раз на тело функции. Осознаем, что у нас нет нужды сохранять множества в явном виде.
  • #58 Заиспользуем boost::function_output_iterator.
  • #59 Поскольку контейнеров в явном виде больше нет, придётся изменить способ вычисления возвращаемого значения. Есть ли проблемы с boost::make_function_output_iterator? Код может не скомпилироваться, т.к. итератор хранит предикат по значению, а у замыкания удалён оператор присваивания. Следовательно, получившийся итератор нельзя присваивать, и формально он не удовлетворяет концепту Iterator.
  • #60 Есть разные способы исправить итератор.
  • #63 Ассемблерный код для двух функций сравнения: с использованием метода rank() – compareRanked, и с использованием цепочки if’ов – compareDirect. Здесь из класса Cookie было исключено строковое поле name, чтобы не загромождать вывод. Скомпилировано x86 gcc 5.3.0 -std=c++14 -O2