Повседневный С++:
алгоритмы и итераторы
Михаил Матросов
mikhail.matrosov@gmail.com
mmatrosov@aligntech.com
1
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 2
goo.gl/TL15Rg
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 33
С++17
С++11/14
С++98
С
High level
Expert level
Modern C++
Almost the same :(
“Within C++ is a smaller, simpler, safer
language struggling to get out”
Bjarne Stroustrup
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 4
High level:
 Парадигма RAII и исключения (exceptions)
 Семантика перемещения
 λ-функции
 Классы и конструкторы
 Простые шаблоны
 STL
 Утилиты и алгоритмы boost
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 5
Expert level:
 Операторы new/delete, владеющие
указатели
 Пользовательские операции копирования
и перемещения
 Пользовательские деструкторы
 Закрытое, защищённое, ромбовидное,
виртуальное наследование
 Шаблонная магия
 Все функции языка Си, препроцессор
 «Голые» циклы
Классический слайд с телом и заголовком
6
Which boost
features overlap
with C++11?
const std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
result.clear();
if (points.size() == 0)
return result;
int p = 0;
bool found = false;
for (int i = 1; i < points.size() && ~found; ++i)
if (points[i - 1].x < 0 && points[i].x >= 0)
{
p = i;
found = true;
}
int q = 0;
found = false;
for (int i = 1; i < points.size() && ~found; ++i) 7
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 8
const std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
result.clear();
if (points.size() == 0)
return result;
int p = 0;
bool found = false;
for (int i = 1; i < points.size() && ~found; ++i)
if (points[i - 1].x < 0 && points[i].x >= 0)
{
p = i;
found = true;
}
int q = 0;
found = false;
for (int i = 1; i < points.size() && ~found; ++i)
if (points[i - 1].x >= 0 && points[i].x < 0)
{
q = i;
found = true;
}
if (p == q)
{
if ((*points.begin()).x >= 0)
return points;
else
return result;
}
int i = p;
while (i != q)
{
if (points[i].x < 0)
{
result.clear();
Point nan;
nan.x = sqrt(-1);
nan.y = sqrt(-1);
result.push_back(nan);
return result;
}
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
i = q;
while (i != p)
{
if (points[i].x >= 0)
{
result.clear();
Point nan;
nan.x = sqrt(-1);
nan.y = sqrt(-1);
result.push_back(nan);
return result;
}
if (++i >= points.size())
i = 0;
}
return std::move(result);
}
std::vector<Point> extractRight(std::vector<Point> points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
if (middle != points.end())
rotate(points, std::next(middle));
if (!is_partitioned(points, isRight))
throw std::runtime_error("Unexpected order");
points.erase(partition_point(points, isRight), points.end());
return points;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 9
const std::vector<Point> extract(const std::vector<Point>& points)
{
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 10
std::vector<Point> extract(const std::vector<Point>& points)
{
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 11
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
result.clear();
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 12
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 13
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
if (points.size() == 0)
return result;
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 14
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
if (points.empty())
return result;
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 15
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
if (points.empty())
return result;
int p = 0;
bool found = false;
for (int i = 1; i < points.size() && ~found; ++i)
if (points[i - 1].x < 0 && points[i].x >= 0)
{
p = i;
found = true;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 16
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
if (points.empty())
return result;
int p = 0;
bool found = false;
for (int i = 1; i < points.size() && !found; ++i)
if (points[i - 1].x < 0 && points[i].x >= 0)
{
p = i;
found = true;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 17
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
if (points.empty())
return result;
int p = 0;
bool found = false;
for (int i = 1; i < points.size(); ++i)
if (points[i - 1].x < 0 && points[i].x >= 0)
{
p = i;
break;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 18
int p = 0;
for (int i = 1; i < points.size(); ++i)
if (points[i - 1].x < 0 && points[i].x >= 0)
{
p = i;
break;
}
int q = 0;
for (int i = 1; i < points.size(); ++i)
if (points[i - 1].x >= 0 && points[i].x < 0)
{
q = i;
break;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 19
auto isRight = [](const Point& pt) { return pt.x >= 0; };
int p = 0;
for (int i = 1; i < points.size(); ++i)
if (!isRight(points[i - 1]) && isRight(points[i]))
{
p = i;
break;
}
int q = 0;
for (int i = 1; i < points.size(); ++i)
if (isRight(points[i - 1]) && !isRight(points[i]))
{
q = i;
break;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 20
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto find = [&](bool flag)
{
for (int i = 1; i < points.size(); ++i)
if (isRight(points[i - 1]) == flag &&
isRight(points[i]) != flag)
return i;
return 0;
};
int p = find(false);
int q = find(true);
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 21
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto findBoundary = [&](bool rightToLeft)
{
for (int i = 1; i < points.size(); ++i)
if (isRight(points[i - 1]) == rightToLeft &&
isRight(points[i]) != rightToLeft)
return i;
return 0;
};
int p = findBoundary(false);
int q = findBoundary(true);
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 22
int p = findBoundary(false);
int q = findBoundary(true);
if (p == q)
{
if (isRight(*points.begin()))
return points;
else
return result;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 23
int p = findBoundary(false);
int q = findBoundary(true);
if (p == q)
{
if (isRight(points[0]))
return points;
else
return result;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 24
int p = findBoundary(false);
int q = findBoundary(true);
if (p == q)
return isRight(points[0]) ? points : result;
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 25
if (p == q)
return isRight(points[0]) ? points : result;
int i = p;
while (i != q)
{
if (!isRight(points[i]))
{
result.clear();
Point nan;
nan.x = sqrt(-1);
nan.y = sqrt(-1);
result.push_back(nan);
return result;
}
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 26
if (p == q)
return isRight(points[0]) ? points : result;
int i = p;
while (i != q)
{
if (!isRight(points[i]))
return { Point(NAN, NAN) };
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
std::numeric_limits::quiet_NaN() vs. std::nan() vs. NAN
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 27
int i = p;
while (i != q)
{
if (!isRight(points[i]))
return { Point(NAN, NAN) };
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
i = q;
while (i != p)
{
if (isRight(points[i]))
return { Point(NAN, NAN) };
if (++i >= points.size())
i = 0;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 28
int i = p;
while (i != q)
{
if (!isRight(points[i]))
return { Point(NAN, NAN) };
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
i = q;
while (i != p)
{
if (isRight(points[i]))
return { Point(NAN, NAN) };
if (++i >= points.size())
i = 0;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 29
auto appendResult = [&](int from, int to, bool shouldBeRight)
{
int i = from;
while (i != to)
{
if (isRight(points[i]) != shouldBeRight)
{
result = { Point(NAN, NAN) };
return false;
}
if (shouldBeRight)
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
return true;
};
bool success = appendResult(p, q, true) && appendResult(q, p, false);
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 30
auto appendResult = [&](int from, int to, bool shouldBeRight)
{
int i = from;
while (i != to)
{
if (isRight(points[i]) != shouldBeRight)
throw std::runtime_error("Unexpected order");
if (shouldBeRight)
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
};
appendResult(p, q, true);
appendResult(q, p, false);
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 31
appendResult(p, q, true);
appendResult(q, p, false);
return std::move(result);
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 32
appendResult(p, q, true);
appendResult(q, p, false);
return result;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 33
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
if (points.empty())
return result;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto findBoundary = [&](bool rightToLeft)
{
for (int i = 1; i < points.size(); ++i)
if (isRight(points[i - 1]) == rightToLeft &&
isRight(points[i]) != rightToLeft)
return i;
return 0;
};
int p = findBoundary(false);
int q = findBoundary(true);
if (p == q)
return isRight(points[0]) ? points : result;
auto appendResult = [&](int from, int to, bool shouldBeRight)
{
int i = from;
while (i != to)
{
if (isRight(points[i]) != shouldBeRight)
throw std::runtime_error("Unexpected order");
if (shouldBeRight)
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
};
appendResult(p, q, true);
appendResult(q, p, false);
return result;
}
const std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
result.clear();
if (points.size() == 0)
return result;
int p = 0;
bool found = false;
for (int i = 1; i < points.size() && ~found; ++i)
if (points[i - 1].x < 0 && points[i].x >= 0)
{
p = i;
found = true;
}
int q = 0;
found = false;
for (int i = 1; i < points.size() && ~found; ++i)
if (points[i - 1].x >= 0 && points[i].x < 0)
{
q = i;
found = true;
}
if (p == q)
{
if ((*points.begin()).x >= 0)
return points;
else
return result;
}
int i = p;
while (i != q)
{
if (points[i].x < 0)
{
result.clear();
Point nan;
nan.x = sqrt(-1);
nan.y = sqrt(-1);
result.push_back(nan);
return result;
}
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
i = q;
while (i != p)
{
if (points[i].x >= 0)
{
result.clear();
Point nan;
nan.x = sqrt(-1);
nan.y = sqrt(-1);
result.push_back(nan);
return result;
}
if (++i >= points.size())
i = 0;
}
return std::move(result);
}
int p = findBoundary(false);
int q = findBoundary(true);
appendResult(p, q, true);
appendResult(q, p, false);
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 34
0 1 2 3 4 5 6 1 2 3 4
0 1 2 3 4 5 6
qp
p q
0 1 2 3 4 5 6 4 5 6 0 1
pq
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 35
0 1 2 3 4 5 6
1. Найти первый элемент
2. Сдвинуть его в началоp
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 36
6 0 1 2 3 4 5
1. Найти первый элемент
2. Сдвинуть его в начало
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 37
5 6 0 1 2 3 4
1. Найти первый элемент
2. Сдвинуть его в начало
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 38
5 6 0 1 2 34
1. Найти первый элемент
2. Сдвинуть его в начало
3. Проверить структуру
4. Выкинуть хвост
5. Вернуть что осталось
std::vector<Point> extractRight(std::vector<Point> points)
{
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 39
std::vector<Point> extractRight(std::vector<Point> points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 40
std::vector<Point> extractRight(std::vector<Point> points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 41
std::vector<Point> extractRight(std::vector<Point> points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
if (middle != points.end())
rotate(points, std::next(middle));
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 42
std::vector<Point> extractRight(std::vector<Point> points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
if (middle != points.end())
rotate(points, std::next(middle));
if (!is_partitioned(points, isRight))
throw std::runtime_error("Unexpected order");
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 43
std::vector<Point> extractRight(std::vector<Point> points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
if (middle != points.end())
rotate(points, std::next(middle));
if (!is_partitioned(points, isRight))
throw std::runtime_error("Unexpected order");
points.erase(partition_point(points, isRight), points.end());
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 44
std::vector<Point> extractRight(std::vector<Point> points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
if (middle != points.end())
rotate(points, std::next(middle));
if (!is_partitioned(points, isRight))
throw std::runtime_error("Unexpected order");
points.erase(partition_point(points, isRight), points.end());
return points;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 45
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 46
const std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
result.clear();
if (points.size() == 0)
return result;
int p = 0;
bool found = false;
for (int i = 1; i < points.size() && ~found; ++i)
if (points[i - 1].x < 0 && points[i].x >= 0)
{
p = i;
found = true;
}
int q = 0;
found = false;
for (int i = 1; i < points.size() && ~found; ++i)
if (points[i - 1].x >= 0 && points[i].x < 0)
{
q = i;
found = true;
}
if (p == q)
{
if ((*points.begin()).x >= 0)
return points;
else
return result;
}
int i = p;
while (i != q)
{
if (points[i].x < 0)
{
result.clear();
Point nan;
nan.x = sqrt(-1);
nan.y = sqrt(-1);
result.push_back(nan);
return result;
}
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
i = q;
while (i != p)
{
if (points[i].x >= 0)
{
result.clear();
Point nan;
nan.x = sqrt(-1);
nan.y = sqrt(-1);
result.push_back(nan);
return result;
}
if (++i >= points.size())
i = 0;
}
return std::move(result);
}
std::vector<Point> extractRight(std::vector<Point> points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
if (middle != points.end())
rotate(points, std::next(middle));
if (!is_partitioned(points, isRight))
throw std::runtime_error("Unexpected order");
points.erase(partition_point(points, isRight), points.end());
return points;
}
std::vector<Point> extract(const std::vector<Point>& points)
{
std::vector<Point> result;
if (points.empty())
return result;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto findBoundary = [&](bool rightToLeft)
{
for (int i = 1; i < points.size(); ++i)
if (isRight(points[i - 1]) == rightToLeft &&
isRight(points[i]) != rightToLeft)
return i;
return 0;
};
int p = findBoundary(false);
int q = findBoundary(true);
if (p == q)
return isRight(points[0]) ? points : result;
auto appendResult = [&](int from, int to, bool shouldBeRight)
{
int i = from;
while (i != to)
{
if (isRight(points[i]) != shouldBeRight)
throw std::runtime_error("Unexpected order");
if (shouldBeRight)
result.push_back(points[i]);
if (++i >= points.size())
i = 0;
}
};
appendResult(p, q, true);
appendResult(q, p, false);
return result;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 47
0 1 2 3
middlefirst last
4 5 6
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 48
Sean Parent: C++ Seasoning (no raw loops)
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 49
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 50
0 1 2 3 4 5 6 0 1 2 3 4
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 51
0 1 2 3 4 5 6
Ответ!
Результат rotate()!
52
template<class It>
class WrappingIterator : public boost::iterator_adaptor<WrappingIterator<It>, It>
{
using Base = boost::iterator_adaptor<WrappingIterator<It>, It>;
public:
WrappingIterator() = default;
WrappingIterator(It it, It begin, It end) :
Base(it), m_end(end), m_size(end - begin) {}
private:
friend class boost::iterator_core_access;
typename Base::reference dereference() const
{
auto it = this->base_reference();
return *(it < m_end ? it : it - m_size);
}
It m_end;
size_t m_size;
};
53
template<class It>
class WrappingIterator : public boost::iterator_facade<WrappingIterator<It>,
typename It::value_type,
boost::random_access_traversal_tag,
typename It::reference>
{
public:
WrappingIterator() = default;
WrappingIterator(It it, It begin, It end) :
m_begin(begin), m_size(end - begin), m_offset(it - begin) {}
template <class OtherIt>
WrappingIterator(const WrappingIterator<OtherIt>& other) :
m_begin(other.m_begin), m_size(other.m_size), m_offset(other.m_offset) {}
private:
friend class boost::iterator_core_access;
template<class> friend class WrappingIterator;
using Base = boost::iterator_facade<WrappingIterator<It>,
typename It::value_type,
boost::random_access_traversal_tag,
typename It::reference>;
// Core interface functions (on the next slide)
It m_begin;
size_t m_size;
size_t m_offset;
};
54
typename Base::reference dereference() const
{
return *(m_begin + (m_offset < m_size ? m_offset : m_offset - m_size));
}
template <class OtherIt>
bool equal(const WrappingIterator<OtherIt>& other) const
{
assert(other.m_begin == m_begin && other.m_size == m_size);
return other.m_offset == m_offset;
}
void advance(typename Base::difference_type n)
{
m_offset += n;
}
void increment()
{
++m_offset;
}
void decrement()
{
--m_offset;
}
template <class OtherIt>
typename Base::difference_type distance_to(const WrappingIterator<OtherIt>& other) const
{
assert(other.m_begin == m_begin && other.m_size == m_size);
return other.m_offset - m_offset;
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 55
template<class It>
auto makeWrappingIterator(It it, It begin, It end)
{
return WrappingIterator<It>(it, begin, end);
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 56
auto extractRight(const std::vector<Point>& points)
{
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 57
auto extractRight(const std::vector<Point>& points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 58
auto extractRight(const std::vector<Point>& points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
middle = middle != points.end() ? std::next(middle) : points.begin();
auto begin = makeWrappingIterator(middle, points.begin(), points.end());
auto rotated = boost::make_iterator_range(begin, begin + points.size());
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 59
auto extractRight(const std::vector<Point>& points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
middle = middle != points.end() ? std::next(middle) : points.begin();
auto begin = makeWrappingIterator(middle, points.begin(), points.end());
auto rotated = boost::make_iterator_range(begin, begin + points.size());
if (!is_partitioned(rotated, isRight))
throw std::runtime_error("Unexpected order");
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 60
auto extractRight(const std::vector<Point>& points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
middle = middle != points.end() ? std::next(middle) : points.begin();
auto begin = makeWrappingIterator(middle, points.begin(), points.end());
auto rotated = boost::make_iterator_range(begin, begin + points.size());
if (!is_partitioned(rotated, isRight))
throw std::runtime_error("Unexpected order");
auto end = partition_point(rotated, isRight);
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 61
auto extractRight(const std::vector<Point>& points)
{
using namespace boost::range;
using namespace boost::algorithm;
auto isRight = [](const Point& pt) { return pt.x >= 0; };
auto middle = adjacent_find(points,
[&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
middle = middle != points.end() ? std::next(middle) : points.begin();
auto begin = makeWrappingIterator(middle, points.begin(), points.end());
auto rotated = boost::make_iterator_range(begin, begin + points.size());
if (!is_partitioned(rotated, isRight))
throw std::runtime_error("Unexpected order");
auto end = partition_point(rotated, isRight);
return boost::make_iterator_range(begin, end);
}
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 62
0 1 2 3 4 5 6
Раз……два!
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 63
template<class It, class Predicate>
auto extractIf(It first, It last, Predicate p)
{
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 64
template<class It, class Predicate>
auto extractIf(It first, It last, Predicate p)
{
using namespace boost::range;
using namespace boost::algorithm;
auto middle = adjacent_find(first, last,
[&](auto&& a, auto&& b) { return !p(a) && p(b); });
middle = middle != last ? std::next(middle) : first;
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 65
template<class It, class Predicate>
auto extractIf(It first, It last, Predicate p)
{
using namespace boost::range;
using namespace boost::algorithm;
auto middle = adjacent_find(first, last,
[&](auto&& a, auto&& b) { return !p(a) && p(b); });
middle = middle != last ? std::next(middle) : first;
auto rotated = boost::join(boost::make_iterator_range(middle, last),
boost::make_iterator_range(first, middle));
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 66
template<class It, class Predicate>
auto extractIf(It first, It last, Predicate p)
{
using namespace boost::range;
using namespace boost::algorithm;
auto middle = adjacent_find(first, last,
[&](auto&& a, auto&& b) { return !p(a) && p(b); });
middle = middle != last ? std::next(middle) : first;
auto rotated = boost::join(boost::make_iterator_range(middle, last),
boost::make_iterator_range(first, middle));
if (!is_partitioned(rotated, p))
throw std::runtime_error("Unexpected order");
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 67
template<class It, class Predicate>
auto extractIf(It first, It last, Predicate p)
{
using namespace boost::range;
using namespace boost::algorithm;
auto middle = adjacent_find(first, last,
[&](auto&& a, auto&& b) { return !p(a) && p(b); });
middle = middle != last ? std::next(middle) : first;
auto rotated = boost::join(boost::make_iterator_range(middle, last),
boost::make_iterator_range(first, middle));
if (!is_partitioned(rotated, p))
throw std::runtime_error("Unexpected order");
auto end = partition_point(rotated, p);
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 68
template<class It, class Predicate>
auto extractIf(It first, It last, Predicate p)
{
using namespace boost::range;
using namespace boost::algorithm;
auto middle = adjacent_find(first, last,
[&](auto&& a, auto&& b) { return !p(a) && p(b); });
middle = middle != last ? std::next(middle) : first;
auto rotated = boost::join(boost::make_iterator_range(middle, last),
boost::make_iterator_range(first, middle));
if (!is_partitioned(rotated, p))
throw std::runtime_error("Unexpected order");
auto end = partition_point(rotated, p);
return boost::make_iterator_range(rotated.begin(), end);
}
 Ответь на вопрос
 Получи мячик
 …
 PROFIT!
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 69
Performance?
Range Traverse time
std::vector 1.0
Joined range of two iterator ranges
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 70
100M elements
double sum = 0;
for (const Point& v : range)
sum += v.x;
Performance?
Range Traverse time
std::vector 1.0
Joined range of two iterator ranges 1.18
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 71
100M elements
double sum = 0;
for (const Point& v : range)
sum += v.x;
"Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 72
 Мыслите в терминах алгоритмов
 Код должен ясно выражать намерение
 Знайте свои инструменты и используйте их к месту
// lambda functions (including generic)
// ternary operator
// exceptions
// transient parameters
std::vector<T>::empty()
adjacent_find()
rotate()
is_partitioned()
partition_point()
std::next();
// custom make-function
// template parameters for iterators
// template parameters for predicates
// function return type deduction
boost::range
boost::algorithm
boost::iterator_adaptor<It>
boost::make_iterator_range()
boost::join()
Спасибо за внимание!

Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017

  • 1.
    Повседневный С++: алгоритмы иитераторы Михаил Матросов mikhail.matrosov@gmail.com mmatrosov@aligntech.com 1
  • 2.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 2
  • 3.
    goo.gl/TL15Rg "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 33 С++17 С++11/14 С++98 С High level Expert level Modern C++ Almost the same :(
  • 4.
    “Within C++ isa smaller, simpler, safer language struggling to get out” Bjarne Stroustrup "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 4
  • 5.
    High level:  ПарадигмаRAII и исключения (exceptions)  Семантика перемещения  λ-функции  Классы и конструкторы  Простые шаблоны  STL  Утилиты и алгоритмы boost "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 5 Expert level:  Операторы new/delete, владеющие указатели  Пользовательские операции копирования и перемещения  Пользовательские деструкторы  Закрытое, защищённое, ромбовидное, виртуальное наследование  Шаблонная магия  Все функции языка Си, препроцессор  «Голые» циклы Классический слайд с телом и заголовком
  • 6.
  • 7.
    const std::vector<Point> extract(conststd::vector<Point>& points) { std::vector<Point> result; result.clear(); if (points.size() == 0) return result; int p = 0; bool found = false; for (int i = 1; i < points.size() && ~found; ++i) if (points[i - 1].x < 0 && points[i].x >= 0) { p = i; found = true; } int q = 0; found = false; for (int i = 1; i < points.size() && ~found; ++i) 7
  • 8.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 8 const std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; result.clear(); if (points.size() == 0) return result; int p = 0; bool found = false; for (int i = 1; i < points.size() && ~found; ++i) if (points[i - 1].x < 0 && points[i].x >= 0) { p = i; found = true; } int q = 0; found = false; for (int i = 1; i < points.size() && ~found; ++i) if (points[i - 1].x >= 0 && points[i].x < 0) { q = i; found = true; } if (p == q) { if ((*points.begin()).x >= 0) return points; else return result; } int i = p; while (i != q) { if (points[i].x < 0) { result.clear(); Point nan; nan.x = sqrt(-1); nan.y = sqrt(-1); result.push_back(nan); return result; } result.push_back(points[i]); if (++i >= points.size()) i = 0; } i = q; while (i != p) { if (points[i].x >= 0) { result.clear(); Point nan; nan.x = sqrt(-1); nan.y = sqrt(-1); result.push_back(nan); return result; } if (++i >= points.size()) i = 0; } return std::move(result); } std::vector<Point> extractRight(std::vector<Point> points) { using namespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); if (middle != points.end()) rotate(points, std::next(middle)); if (!is_partitioned(points, isRight)) throw std::runtime_error("Unexpected order"); points.erase(partition_point(points, isRight), points.end()); return points; }
  • 9.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 9 const std::vector<Point> extract(const std::vector<Point>& points) {
  • 10.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 10 std::vector<Point> extract(const std::vector<Point>& points) {
  • 11.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 11 std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; result.clear();
  • 12.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 12 std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result;
  • 13.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 13 std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; if (points.size() == 0) return result;
  • 14.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 14 std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; if (points.empty()) return result;
  • 15.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 15 std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; if (points.empty()) return result; int p = 0; bool found = false; for (int i = 1; i < points.size() && ~found; ++i) if (points[i - 1].x < 0 && points[i].x >= 0) { p = i; found = true; }
  • 16.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 16 std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; if (points.empty()) return result; int p = 0; bool found = false; for (int i = 1; i < points.size() && !found; ++i) if (points[i - 1].x < 0 && points[i].x >= 0) { p = i; found = true; }
  • 17.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 17 std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; if (points.empty()) return result; int p = 0; bool found = false; for (int i = 1; i < points.size(); ++i) if (points[i - 1].x < 0 && points[i].x >= 0) { p = i; break; }
  • 18.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 18 int p = 0; for (int i = 1; i < points.size(); ++i) if (points[i - 1].x < 0 && points[i].x >= 0) { p = i; break; } int q = 0; for (int i = 1; i < points.size(); ++i) if (points[i - 1].x >= 0 && points[i].x < 0) { q = i; break; }
  • 19.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 19 auto isRight = [](const Point& pt) { return pt.x >= 0; }; int p = 0; for (int i = 1; i < points.size(); ++i) if (!isRight(points[i - 1]) && isRight(points[i])) { p = i; break; } int q = 0; for (int i = 1; i < points.size(); ++i) if (isRight(points[i - 1]) && !isRight(points[i])) { q = i; break; }
  • 20.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 20 auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto find = [&](bool flag) { for (int i = 1; i < points.size(); ++i) if (isRight(points[i - 1]) == flag && isRight(points[i]) != flag) return i; return 0; }; int p = find(false); int q = find(true);
  • 21.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 21 auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto findBoundary = [&](bool rightToLeft) { for (int i = 1; i < points.size(); ++i) if (isRight(points[i - 1]) == rightToLeft && isRight(points[i]) != rightToLeft) return i; return 0; }; int p = findBoundary(false); int q = findBoundary(true);
  • 22.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 22 int p = findBoundary(false); int q = findBoundary(true); if (p == q) { if (isRight(*points.begin())) return points; else return result; }
  • 23.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 23 int p = findBoundary(false); int q = findBoundary(true); if (p == q) { if (isRight(points[0])) return points; else return result; }
  • 24.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 24 int p = findBoundary(false); int q = findBoundary(true); if (p == q) return isRight(points[0]) ? points : result;
  • 25.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 25 if (p == q) return isRight(points[0]) ? points : result; int i = p; while (i != q) { if (!isRight(points[i])) { result.clear(); Point nan; nan.x = sqrt(-1); nan.y = sqrt(-1); result.push_back(nan); return result; } result.push_back(points[i]); if (++i >= points.size()) i = 0; }
  • 26.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 26 if (p == q) return isRight(points[0]) ? points : result; int i = p; while (i != q) { if (!isRight(points[i])) return { Point(NAN, NAN) }; result.push_back(points[i]); if (++i >= points.size()) i = 0; } std::numeric_limits::quiet_NaN() vs. std::nan() vs. NAN
  • 27.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 27 int i = p; while (i != q) { if (!isRight(points[i])) return { Point(NAN, NAN) }; result.push_back(points[i]); if (++i >= points.size()) i = 0; } i = q; while (i != p) { if (isRight(points[i])) return { Point(NAN, NAN) }; if (++i >= points.size()) i = 0; }
  • 28.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 28 int i = p; while (i != q) { if (!isRight(points[i])) return { Point(NAN, NAN) }; result.push_back(points[i]); if (++i >= points.size()) i = 0; } i = q; while (i != p) { if (isRight(points[i])) return { Point(NAN, NAN) }; if (++i >= points.size()) i = 0; }
  • 29.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 29 auto appendResult = [&](int from, int to, bool shouldBeRight) { int i = from; while (i != to) { if (isRight(points[i]) != shouldBeRight) { result = { Point(NAN, NAN) }; return false; } if (shouldBeRight) result.push_back(points[i]); if (++i >= points.size()) i = 0; } return true; }; bool success = appendResult(p, q, true) && appendResult(q, p, false);
  • 30.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 30 auto appendResult = [&](int from, int to, bool shouldBeRight) { int i = from; while (i != to) { if (isRight(points[i]) != shouldBeRight) throw std::runtime_error("Unexpected order"); if (shouldBeRight) result.push_back(points[i]); if (++i >= points.size()) i = 0; } }; appendResult(p, q, true); appendResult(q, p, false);
  • 31.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 31 appendResult(p, q, true); appendResult(q, p, false); return std::move(result); }
  • 32.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 32 appendResult(p, q, true); appendResult(q, p, false); return result; }
  • 33.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 33 std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; if (points.empty()) return result; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto findBoundary = [&](bool rightToLeft) { for (int i = 1; i < points.size(); ++i) if (isRight(points[i - 1]) == rightToLeft && isRight(points[i]) != rightToLeft) return i; return 0; }; int p = findBoundary(false); int q = findBoundary(true); if (p == q) return isRight(points[0]) ? points : result; auto appendResult = [&](int from, int to, bool shouldBeRight) { int i = from; while (i != to) { if (isRight(points[i]) != shouldBeRight) throw std::runtime_error("Unexpected order"); if (shouldBeRight) result.push_back(points[i]); if (++i >= points.size()) i = 0; } }; appendResult(p, q, true); appendResult(q, p, false); return result; } const std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; result.clear(); if (points.size() == 0) return result; int p = 0; bool found = false; for (int i = 1; i < points.size() && ~found; ++i) if (points[i - 1].x < 0 && points[i].x >= 0) { p = i; found = true; } int q = 0; found = false; for (int i = 1; i < points.size() && ~found; ++i) if (points[i - 1].x >= 0 && points[i].x < 0) { q = i; found = true; } if (p == q) { if ((*points.begin()).x >= 0) return points; else return result; } int i = p; while (i != q) { if (points[i].x < 0) { result.clear(); Point nan; nan.x = sqrt(-1); nan.y = sqrt(-1); result.push_back(nan); return result; } result.push_back(points[i]); if (++i >= points.size()) i = 0; } i = q; while (i != p) { if (points[i].x >= 0) { result.clear(); Point nan; nan.x = sqrt(-1); nan.y = sqrt(-1); result.push_back(nan); return result; } if (++i >= points.size()) i = 0; } return std::move(result); } int p = findBoundary(false); int q = findBoundary(true); appendResult(p, q, true); appendResult(q, p, false);
  • 34.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 34 0 1 2 3 4 5 6 1 2 3 4 0 1 2 3 4 5 6 qp p q 0 1 2 3 4 5 6 4 5 6 0 1 pq
  • 35.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 35 0 1 2 3 4 5 6 1. Найти первый элемент 2. Сдвинуть его в началоp
  • 36.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 36 6 0 1 2 3 4 5 1. Найти первый элемент 2. Сдвинуть его в начало
  • 37.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 37 5 6 0 1 2 3 4 1. Найти первый элемент 2. Сдвинуть его в начало
  • 38.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 38 5 6 0 1 2 34 1. Найти первый элемент 2. Сдвинуть его в начало 3. Проверить структуру 4. Выкинуть хвост 5. Вернуть что осталось
  • 39.
    std::vector<Point> extractRight(std::vector<Point> points) { "ПовседневныйС++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 39
  • 40.
    std::vector<Point> extractRight(std::vector<Point> points) { usingnamespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 40
  • 41.
    std::vector<Point> extractRight(std::vector<Point> points) { usingnamespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 41
  • 42.
    std::vector<Point> extractRight(std::vector<Point> points) { usingnamespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); if (middle != points.end()) rotate(points, std::next(middle)); "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 42
  • 43.
    std::vector<Point> extractRight(std::vector<Point> points) { usingnamespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); if (middle != points.end()) rotate(points, std::next(middle)); if (!is_partitioned(points, isRight)) throw std::runtime_error("Unexpected order"); "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 43
  • 44.
    std::vector<Point> extractRight(std::vector<Point> points) { usingnamespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); if (middle != points.end()) rotate(points, std::next(middle)); if (!is_partitioned(points, isRight)) throw std::runtime_error("Unexpected order"); points.erase(partition_point(points, isRight), points.end()); "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 44
  • 45.
    std::vector<Point> extractRight(std::vector<Point> points) { usingnamespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); if (middle != points.end()) rotate(points, std::next(middle)); if (!is_partitioned(points, isRight)) throw std::runtime_error("Unexpected order"); points.erase(partition_point(points, isRight), points.end()); return points; } "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 45
  • 46.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 46 const std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; result.clear(); if (points.size() == 0) return result; int p = 0; bool found = false; for (int i = 1; i < points.size() && ~found; ++i) if (points[i - 1].x < 0 && points[i].x >= 0) { p = i; found = true; } int q = 0; found = false; for (int i = 1; i < points.size() && ~found; ++i) if (points[i - 1].x >= 0 && points[i].x < 0) { q = i; found = true; } if (p == q) { if ((*points.begin()).x >= 0) return points; else return result; } int i = p; while (i != q) { if (points[i].x < 0) { result.clear(); Point nan; nan.x = sqrt(-1); nan.y = sqrt(-1); result.push_back(nan); return result; } result.push_back(points[i]); if (++i >= points.size()) i = 0; } i = q; while (i != p) { if (points[i].x >= 0) { result.clear(); Point nan; nan.x = sqrt(-1); nan.y = sqrt(-1); result.push_back(nan); return result; } if (++i >= points.size()) i = 0; } return std::move(result); } std::vector<Point> extractRight(std::vector<Point> points) { using namespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); if (middle != points.end()) rotate(points, std::next(middle)); if (!is_partitioned(points, isRight)) throw std::runtime_error("Unexpected order"); points.erase(partition_point(points, isRight), points.end()); return points; } std::vector<Point> extract(const std::vector<Point>& points) { std::vector<Point> result; if (points.empty()) return result; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto findBoundary = [&](bool rightToLeft) { for (int i = 1; i < points.size(); ++i) if (isRight(points[i - 1]) == rightToLeft && isRight(points[i]) != rightToLeft) return i; return 0; }; int p = findBoundary(false); int q = findBoundary(true); if (p == q) return isRight(points[0]) ? points : result; auto appendResult = [&](int from, int to, bool shouldBeRight) { int i = from; while (i != to) { if (isRight(points[i]) != shouldBeRight) throw std::runtime_error("Unexpected order"); if (shouldBeRight) result.push_back(points[i]); if (++i >= points.size()) i = 0; } }; appendResult(p, q, true); appendResult(q, p, false); return result; }
  • 47.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 47 0 1 2 3 middlefirst last 4 5 6
  • 48.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 48 Sean Parent: C++ Seasoning (no raw loops)
  • 49.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 49
  • 50.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 50
  • 51.
    0 1 23 4 5 6 0 1 2 3 4 "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 51 0 1 2 3 4 5 6 Ответ! Результат rotate()!
  • 52.
    52 template<class It> class WrappingIterator: public boost::iterator_adaptor<WrappingIterator<It>, It> { using Base = boost::iterator_adaptor<WrappingIterator<It>, It>; public: WrappingIterator() = default; WrappingIterator(It it, It begin, It end) : Base(it), m_end(end), m_size(end - begin) {} private: friend class boost::iterator_core_access; typename Base::reference dereference() const { auto it = this->base_reference(); return *(it < m_end ? it : it - m_size); } It m_end; size_t m_size; };
  • 53.
    53 template<class It> class WrappingIterator: public boost::iterator_facade<WrappingIterator<It>, typename It::value_type, boost::random_access_traversal_tag, typename It::reference> { public: WrappingIterator() = default; WrappingIterator(It it, It begin, It end) : m_begin(begin), m_size(end - begin), m_offset(it - begin) {} template <class OtherIt> WrappingIterator(const WrappingIterator<OtherIt>& other) : m_begin(other.m_begin), m_size(other.m_size), m_offset(other.m_offset) {} private: friend class boost::iterator_core_access; template<class> friend class WrappingIterator; using Base = boost::iterator_facade<WrappingIterator<It>, typename It::value_type, boost::random_access_traversal_tag, typename It::reference>; // Core interface functions (on the next slide) It m_begin; size_t m_size; size_t m_offset; };
  • 54.
    54 typename Base::reference dereference()const { return *(m_begin + (m_offset < m_size ? m_offset : m_offset - m_size)); } template <class OtherIt> bool equal(const WrappingIterator<OtherIt>& other) const { assert(other.m_begin == m_begin && other.m_size == m_size); return other.m_offset == m_offset; } void advance(typename Base::difference_type n) { m_offset += n; } void increment() { ++m_offset; } void decrement() { --m_offset; } template <class OtherIt> typename Base::difference_type distance_to(const WrappingIterator<OtherIt>& other) const { assert(other.m_begin == m_begin && other.m_size == m_size); return other.m_offset - m_offset; }
  • 55.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 55 template<class It> auto makeWrappingIterator(It it, It begin, It end) { return WrappingIterator<It>(it, begin, end); }
  • 56.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 56 auto extractRight(const std::vector<Point>& points) {
  • 57.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 57 auto extractRight(const std::vector<Point>& points) { using namespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); });
  • 58.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 58 auto extractRight(const std::vector<Point>& points) { using namespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); middle = middle != points.end() ? std::next(middle) : points.begin(); auto begin = makeWrappingIterator(middle, points.begin(), points.end()); auto rotated = boost::make_iterator_range(begin, begin + points.size());
  • 59.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 59 auto extractRight(const std::vector<Point>& points) { using namespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); middle = middle != points.end() ? std::next(middle) : points.begin(); auto begin = makeWrappingIterator(middle, points.begin(), points.end()); auto rotated = boost::make_iterator_range(begin, begin + points.size()); if (!is_partitioned(rotated, isRight)) throw std::runtime_error("Unexpected order");
  • 60.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 60 auto extractRight(const std::vector<Point>& points) { using namespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); middle = middle != points.end() ? std::next(middle) : points.begin(); auto begin = makeWrappingIterator(middle, points.begin(), points.end()); auto rotated = boost::make_iterator_range(begin, begin + points.size()); if (!is_partitioned(rotated, isRight)) throw std::runtime_error("Unexpected order"); auto end = partition_point(rotated, isRight);
  • 61.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 61 auto extractRight(const std::vector<Point>& points) { using namespace boost::range; using namespace boost::algorithm; auto isRight = [](const Point& pt) { return pt.x >= 0; }; auto middle = adjacent_find(points, [&](auto&& pt1, auto&& pt2) { return !isRight(pt1) && isRight(pt2); }); middle = middle != points.end() ? std::next(middle) : points.begin(); auto begin = makeWrappingIterator(middle, points.begin(), points.end()); auto rotated = boost::make_iterator_range(begin, begin + points.size()); if (!is_partitioned(rotated, isRight)) throw std::runtime_error("Unexpected order"); auto end = partition_point(rotated, isRight); return boost::make_iterator_range(begin, end); }
  • 62.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 62 0 1 2 3 4 5 6 Раз……два!
  • 63.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 63 template<class It, class Predicate> auto extractIf(It first, It last, Predicate p) {
  • 64.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 64 template<class It, class Predicate> auto extractIf(It first, It last, Predicate p) { using namespace boost::range; using namespace boost::algorithm; auto middle = adjacent_find(first, last, [&](auto&& a, auto&& b) { return !p(a) && p(b); }); middle = middle != last ? std::next(middle) : first;
  • 65.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 65 template<class It, class Predicate> auto extractIf(It first, It last, Predicate p) { using namespace boost::range; using namespace boost::algorithm; auto middle = adjacent_find(first, last, [&](auto&& a, auto&& b) { return !p(a) && p(b); }); middle = middle != last ? std::next(middle) : first; auto rotated = boost::join(boost::make_iterator_range(middle, last), boost::make_iterator_range(first, middle));
  • 66.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 66 template<class It, class Predicate> auto extractIf(It first, It last, Predicate p) { using namespace boost::range; using namespace boost::algorithm; auto middle = adjacent_find(first, last, [&](auto&& a, auto&& b) { return !p(a) && p(b); }); middle = middle != last ? std::next(middle) : first; auto rotated = boost::join(boost::make_iterator_range(middle, last), boost::make_iterator_range(first, middle)); if (!is_partitioned(rotated, p)) throw std::runtime_error("Unexpected order");
  • 67.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 67 template<class It, class Predicate> auto extractIf(It first, It last, Predicate p) { using namespace boost::range; using namespace boost::algorithm; auto middle = adjacent_find(first, last, [&](auto&& a, auto&& b) { return !p(a) && p(b); }); middle = middle != last ? std::next(middle) : first; auto rotated = boost::join(boost::make_iterator_range(middle, last), boost::make_iterator_range(first, middle)); if (!is_partitioned(rotated, p)) throw std::runtime_error("Unexpected order"); auto end = partition_point(rotated, p);
  • 68.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 68 template<class It, class Predicate> auto extractIf(It first, It last, Predicate p) { using namespace boost::range; using namespace boost::algorithm; auto middle = adjacent_find(first, last, [&](auto&& a, auto&& b) { return !p(a) && p(b); }); middle = middle != last ? std::next(middle) : first; auto rotated = boost::join(boost::make_iterator_range(middle, last), boost::make_iterator_range(first, middle)); if (!is_partitioned(rotated, p)) throw std::runtime_error("Unexpected order"); auto end = partition_point(rotated, p); return boost::make_iterator_range(rotated.begin(), end); }
  • 69.
     Ответь навопрос  Получи мячик  …  PROFIT! "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 69
  • 70.
    Performance? Range Traverse time std::vector1.0 Joined range of two iterator ranges "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 70 100M elements double sum = 0; for (const Point& v : range) sum += v.x;
  • 71.
    Performance? Range Traverse time std::vector1.0 Joined range of two iterator ranges 1.18 "Повседневный С++: алгоритмы и итераторы", Михаил Матросов, Конференция C++ Russia 2017 71 100M elements double sum = 0; for (const Point& v : range) sum += v.x;
  • 72.
    "Повседневный С++: алгоритмыи итераторы", Михаил Матросов, Конференция C++ Russia 2017 72  Мыслите в терминах алгоритмов  Код должен ясно выражать намерение  Знайте свои инструменты и используйте их к месту // lambda functions (including generic) // ternary operator // exceptions // transient parameters std::vector<T>::empty() adjacent_find() rotate() is_partitioned() partition_point() std::next(); // custom make-function // template parameters for iterators // template parameters for predicates // function return type deduction boost::range boost::algorithm boost::iterator_adaptor<It> boost::make_iterator_range() boost::join() Спасибо за внимание!

Editor's Notes

  • #2 ToDo: add links
  • #3 Разрабатываю на С++ в течение 12 лет, занимался обработкой изображений и 3D графикой. В настоящий момент технический менеджер в компании Align Technology. Это международная компания, производитель медицинского оборудования для нужд ортодонтии. Использует ПО, разработанное на С++. Я занимаюсь развитием платформы 3D CAD приложения для планирования ортодонтического лечения. Пытаюсь облегчить жизнь разработчиков, которые ей пользуются.
  • #6 По поводу фичей, взятых из boost: http://stackoverflow.com/q/8851670/261217
  • #7 1. Самое важное, что появилось в С++11 и пришло оно из boost?
  • #8 Исходный текст функции.
  • #10 1. No reason to return container by const value
  • #12 1. No reason to clear container after it is default constructed
  • #14 1. Use empty()
  • #16 В каких случаях выражение ~found может обращаться в false? Какой у него тип?
  • #17 found не нужен, т.к. мы можем его вывести через i
  • #19 Logic is duplicated
  • #20 Logic is duplicated
  • #21 Bad names
  • #24 How [0] and front() are different?
  • #26 Side effects of sqrt(-1)?
  • #27 Why do we need NAN when we have std::numeric_limits<flaot>::quiet_NaN()? How returning braced list might be worse than clear and return existing vector? (NRVO) ToDo: check this.
  • #29 Similar code snippets most probably serve similar purpose, and thus needs to be represented by a single entity.
  • #31 What other alternatives do we have to represent an error?
  • #32 RVO is disabled because standard says that type of returned value and return type of function should be the same.
  • #36 Algorithm to find first element - adjacent_find(). adjacent_find() returns iterator to the first element in the pair, how to retrieve the second? How is std::next() better than it+1? How is different from ++it?
  • #37 1. Algorithm to move it to beginning – rotate()
  • #39 Algorithm to check structure – is_partitioned() Algorithm to detect the tail – partition_point()
  • #40 1. In which scenario this signature is preferable?
  • #42 Why do we need & in []? What exactly this lambda function correspond to?
  • #52 Механизмы для создания виртуальных последовательностей? Как сделать свой итератор?
  • #53 Why do we need Base:: and this->? Can we use return type deduction here? Why it is UB?
  • #54 Why we need template constructor?
  • #55 Why some functions are templates?
  • #56 Зачем нам нужна эта функция? Можно ли будет в будущем от неё отказаться?
  • #57 How to disallow binding to temporary objects?
  • #59 ToDo: rotated = boost::make_iterator_range(begin, end);
  • #60 Why it is not necessary to specify std:: before is_partitioned()?
  • #62 What is the next logical step to generalization? Which of these algorithms (including rotate()) benefit from random access iterator?
  • #72 Additional questions for winners: Что возвращает rotate()? Безопасно ли объединять два временных диапазона? Как проще всего присвоить какое-либо значение N элементам в конце массива?