Слайд 4Семантика перемещения
Позволяет компилятору заменить дорогостоящую операцию копирования менее дорогими перемещениями
Копирование
vector, string
Время: O(N)
Память: O(N) – для хранения копии
Может выбросить
исключение
Перемещение vector, string
Время: O(1)
Память: O(1)
Как правило, не бросает исключений
Некоторые типы могут только перемещаться
std::fstream, std::unique_ptr, std::future, std::thread
Слайд 5struct Person {
string name;
string surname;
};
struct Department {
string
name;
vector employees;
};
Department ReadDepartment(istream& input) {
Department department;
Person p;
if (getline(input, department.name)) {
string line;
while (getline(input, line)) {
istringstream strm(line);
if (strm >> p.name && strm >> p.surname)
department.employees.push_back(p);
}
}
return department;
}
C++’03
Может приводить к копированию всего Department
Копирование сотрудников при изменерии размеров контейнера
Слайд 8struct Person {
string name;
string surname;
};
struct Department {
string
name;
vector employees;
};
Department ReadDepartment(istream& input) {
Department department;
Person p;
if (getline(input, department.name)) {
string line;
while (getline(input, line)) {
istringstream strm(line);
if (strm >> p.name && strm >> p.surname)
department.employees.emplace_back(move(p));
}
}
return department;
}
C++’14
Элемент конструируется путем перемещения значения из p
Слайд 10namespace detail {
template
class Handle {
HANDLE m_handle =
INVALID;
public:
Handle() = default;
explicit Handle(HANDLE handle = INVALID) :
m_handle(handle) {}
Handle(Handle&& x) : m_handle(x.m_handle) { x.m_handle = INVALID; }
Handle(const Handle&) = delete; // no copy construction
Handle& operator=(Handle&& x) {// move assignment
std::swap(m_handle, x.m_handle);
return *this;
}
Handle &operator=(const Handle&) = delete; // no copy assignment
Слайд 12FileHandle SafeOpenFile(LPCWSTR fileName) {
FileHandle fh(CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
if (!fh)
throw runtime_error("Failed to open file");
return fh;
}
int main() {
auto f = SafeOpenFile(L"main.cpp");
DWORD fileSize = GetFileSize(f, nullptr);
if (fileSize != INVALID_FILE_SIZE)
cout << "File size: " << fileSize << "\n";
}
Пример использования
Слайд 26enable_shared_from_this
Позволяет объекту, управляемому shared_ptr, безопасно создать экземпляр shared_ptr на самого
себя
shared_from_this()
weak_from_this () (C++ 17)
Нельзя вызывать shared_from_this() из конструктора и деструктора,
а также у объекта, не обернутого в shared_ptr
До C++ 17 грозило UB
В C++17 – исключение bad_weak_ptr
Применимость
Передача shared-ссылки на самого себя
Реализация идиомы weak this
Защита от преждевременного удаления при вызове внешнего кода
Слайд 29Решение, которое (иногда) не работает
Почему?
using Callback = function
image)>;
void LoadImageAsync(string_view url, Callback callback);
struct ImageView
{
void ShowImageAtURL (string_view url)
{
LoadImageAsync(url, [this](auto url, auto img) {
OnImageLoaded(url, move(img));
});
}
private:
void OnImageLoaded(string_view url, unique_ptr
![]()
image);
};
UB На момент вызова колбека ImageView может быть разрушен. Нельзя вызывать OnImageLoaded
Слайд 31Немного сахара в C++17
struct ImageView : enable_shared_from_this
{
static auto Create()
{
return shared_ptr(new ImageView);
}
void ShowImageAtURL (string_view url)
{
auto weakSelf = weak_from_this();
LoadImageAsync(url, [=](auto url, auto data) {
if (auto strongSelf = weakSelf.lock())
strongSelf->OnImageLoaded(url, move(data));
});
}
private:
ImageView() = default;
void OnImageLoaded(string_view url, unique_ptr
![]()
image);
};
Слайд 32Еще больше сахара с BindWeakPtr
struct ImageView : enable_shared_from_this
{
static auto
Create() {
return shared_ptr(new ImageView);
}
void ShowImageAtURL (string_view
url) {
using namespace std::placeholders;
LoadImageAsync(url, BindWeakPtr(&ImageView::OnImageLoaded,
shared_from_this(), _1, _2));
}
private:
ImageView() = default;
void OnImageLoaded(string_view url, unique_ptr
![]()
image);
};
Или weak_from_this()
Bind weak ptr: https://goo.gl/5NquDA
Слайд 33Пример - UniversalPtr
Указатель, инкапсулирующий семантику владения ресурсом
No ownership
Unique/shared ownership
Применимость –
альтернатива передаче по ссылке или указателю
Объекту нужен доступ к ресурсу,
но не нужны права владения
Lifetime объекта не превышает lifetime ресурса
Клиент может передать объекту ресурс вместе с правами владения
Накладные расходы
Обертка над shared_ptr с соответствующими затратами на копирование
Слайд 35struct IShape {
virtual ~IShape() = default;
virtual void Draw()const
= 0;
};
struct IShapeFactory {
virtual ~IShapeFactory() = default;
virtual unique_ptr
CreateShape(string name) = 0;
};
struct ShapeFactory : IShapeFactory { /* implementation details */ };
struct Painter {
Painter(UniversalPtr factory)
:m_factory(move(factory)) {}
void WorkWithShapes() {
m_factory->CreateShape("circle")->Draw();
}
private:
UniversalPtr m_factory;
};
Слайд 36void PainterWithoutOwnership()
{
ShapeFactory factory;
Painter c1(factory);
Painter c2(&factory);
c1.WorkWithShapes();
}
Painter GetCliPainterWithUniqueAccessToFactory()
{
return Painter(make_unique());
}
vector PainterWithSharedAccessToFactory()
{
auto f = make_shared();
Painter c1(f), c2(f),
c3(f);
return { c1, c2, c3 };
}
Слайд 37string GetFileContent(const char *fileName);
class DocumentStorage : public enable_shared_from_this {
using
StringPtr = shared_ptr;
using StringWeakPtr = weak_ptr;
map m_items;
public:
using DocumentContent = function
;
DocumentContent GetDocumentContent(const string &fileName) {
...
}
};
Пример – простой кеш документов
Слайд 38 DocumentContent GetDocumentContent(const string &fileName) {
StringPtr content;
auto it = m_items.find(fileName);
if (it != m_items.end())
content = it->second.lock();
if (!content) {
weak_ptr weakSelf = shared_from_this();
auto deleter = [weakSelf, fileName](string *s) {
delete s;
if (auto strongSelf = weakSelf.lock())
strongSelf->m_items.erase(fileName);
};
content.reset(new string(GetFileContent(fileName.c_str())), deleter);
m_items.insert_or_assign(fileName, content);
}
return [content] { return *content; };
}
};
Слайд 39int main() {
auto storage = make_shared();
{
auto
content1 = storage->GetDocumentContent("main.cpp");
cout
auto content2 = storage->GetDocumentContent("main.cpp");
cout << content2().length() << endl;
}
{
auto content1 = storage->GetDocumentContent("main.cpp");
cout << content1().length() << endl;
}
}
Loading file content main.cpp
1736
1736
Loading file content main.cpp
1736
Слайд 41optional
Опциональное значение
Не использует динамическое выделение памяти
Применение
Значение, которого может и не
быть
Результат поиска
Undefined-значение (если сам тип его не имеет)
Отложенное конструирование объекта
Поле
класса не может быть проинициализировано в конструкторе
Ленивые вычисления
Простейшее информирование об ошибке
Слайд 42struct ICanvas;
struct Rect
{
void Draw(ICanvas & canvas) const {
if (fillColor) canvas.BeginSolidFill(*fillColor);
canvas.SetLineColor(lineColor);
canvas.MoveTo(left, top);
canvas.LineTo(left
+ width, top);
canvas.LineTo(left + width, top + height);
canvas.LineTo(left, top + height);
canvas.LineTo(left, top);
if (fillColor) canvas.EndFill();
}
float left = 0.f, top = 0.f, width = 0.f, height = 0.f;
optional
fillColor, lineColor;
};
Прямоугольник с опциональными заливкой и обводкой
Слайд 43struct ICanvas
{
virtual ~ICanvas() = default;
virtual void BeginSolidFill(const RGBAColor&
fillColor) = 0;
virtual void EndFill() = 0;
virtual void
MoveTo(float x, float y) = 0;
virtual void SetLineColor(const optional
& lineColor) = 0;
virtual void LineTo(float x, float y) = 0;
};
void DrawPicture(ICanvas& canvas)
{
Rect{ 10, 10, 20, 10, palette::YELLOW, palette::RED }.Draw(canvas);
Rect{ 5, 5, 10, 10, nullopt, palette::GREEN }.Draw(canvas);
Rect{ 20, 5, 5, 20, palette::BLUE }.Draw(canvas);
}
Слайд 44struct Signal
{
double GetMaxAmplitude()const {
if (!m_maxAmplitude) {
m_maxAmplitude = m_samples.empty() ? 0.0
: *max_element(m_samples.begin(), m_samples.end());
}
return *m_maxAmplitude;
}
void SetSamples(vector
samples) {
m_samples = move(samples);
m_maxAmplitude.reset();
}
private:
vector m_samples;
mutable optional m_maxAmplitude;
};
Ленивое вычисление характеристик цифрового сигнала
Слайд 46variant
Тип, способный хранить значение одного из нескольких типов
Хранение по значению
Примитивные
или составные типы
Не требуется какая-либо связь между типами
Достоинства
Типобезопасность по сравнению
с union
Не использует динамическое выделение памяти
Надежнее по сравнению с наивной реализацией
Слайд 47Пример – решение квадратного уравнения
struct NotAQudaraticEquation {};
using QuadraticEquationRoots = variant
std::monostate, // no roots
double, // one root
pair, // two roots
NotAQudaraticEquation>; // not a quadratic equation
Слайд 48Решение квадратного уравнения
QuadraticEquationRoots Solve(double a, double b, double c) {
if (abs(a) < std::numeric_limits::epsilon())
return NotAQudaraticEquation{};
else {
auto disc = b * b - 4 * a * c;
if (disc < 0)
return std::monostate{};
else if (disc < std::numeric_limits::epsilon())
return -b / (2 * a);
else {
auto sqrtDisc = sqrt(disc);
return make_pair((-b - sqrtDisc) / (2 * a), (-b + sqrtDisc) / (2 * a));
}
}
}
Слайд 49Обработка при помощи visitor class
struct ResultPrinter {
void operator()(std::monostate) {
cout
{
cout << "1 root:" << x << "\n";
}
void operator()(const pair& twoRoots) {
cout << "2 roots: " << twoRoots.first << ", " << twoRoots.second << "\n";
}
void operator()(NotAQudaraticEquation) {
cout << "This is not a quadratic equation\n";
}
};
visit(ResultPrinter(), Solve(1, 0, -1));
Слайд 50Обработка при помощи get_if
auto result = Solve(1, 0, -1);
if (get_if(&result))
cout
cout << "1 root:" << *singleRoot << "\n";
else if (auto twoRoots = get_if
>(&result))
cout << "2 roots: " << twoRoots->first << ", " << twoRoots->second << "\n";
else if (get_if(&result))
cout << "This is not a quadratic equation\n";
else
cout << "Valueless by exception\n";
get_if для boost::variant https://goo.gl/2cFj2y
Слайд 51Обработка при помощи constexpr if
template struct always_false : std::false_type
{};
auto visitor = [](auto&& arg) {
using T = decay_t;
if constexpr (is_same_v)
cout << "No real roots\n";
else if constexpr (is_same_v)
cout << "1 root:" << arg << "\n";
else if constexpr (is_same_v>)
cout << "Two roots: " << arg.first << ", " << arg.second << "\n";
else if constexpr (is_same_v)
cout << "This is not a quadratic equation\n";
else
static_assert(always_false::value, "non-exhaustive visitor!");
};
visit(visitor, Solve(1, 0, 0));
Слайд 53Контейнеры
Хранят данные
Алгоритмы (~100 в C++17)
Поиск и подсчет элементов
Модификация, копирование элементов,
перестановки
Сортировка и разделение
Удаление элементов
Сравнение
Параллельная обработка (c++17)
Диапазоны
Обещают в C++20
Есть в библиотеке
boost::range
Слайд 54Исходные данные
enum class Gender : uint8_t { Male, Female };
struct
Person {
string name;
short age;
Gender gender;
int salary;
};
ostream&
operator<<(ostream& out, const Person& p);
int main() {
const vector
people = {
{ "Ivan", 25, Gender::Male }, { "Peter", 35, Gender::Male },
/* some more people*/
};
…
};
Слайд 55Вывод в stdout
for (size_t i = 0; i < people.size();
++i) {
cout
: people) {
cout << person << "\n";
}
copy(people, ostream_iterator
(cout, "\n"));
Слайд 56Вывести самого старого (raw loop)
if (!people.empty()) {
size_t oldest =
0;
for (size_t i = 1; i < people.size(); ++i)
{
if (people[i].age > people[oldest].age) {
oldest = i;
}
}
cout << "The oldest one is " << people[oldest] << "\n";
}
else {
cout << "No people\n";
}
Слайд 57Вывести самого старого (max_element)
auto orderedByAge = [](auto & lhs, auto
& rhs) {
return lhs.age < rhs.age;
};
auto oldest = max_element(people,
orderedByAge);
if (oldest != people.end())
cout << "The oldest one is " << *oldest << "\n";
else
cout << "No people\n";
Слайд 58Вывести самого старого (C++17)
auto orderedByAge = [](auto & lhs, auto
& rhs) {
return lhs.age < rhs.age;
};
oldest !=
people.end())
cout << "The oldest one is " << *oldest << "\n";
else
cout << "No people\n";
auto oldest = max_element(people, orderedByAge);
if (
Слайд 59Вывести женщин от 20 до 30 лет, а потом мужчин
от 25 до 40
for (size_t i = 0; i
people.size(); ++i) {
if (people[i].gender == Gender::Female &&
(people[i].age >= 20 && people[i].age <= 30)) {
cout << people[i] << "\n";
}
}
for (size_t i = 0; i < people.size(); ++i) {
if (people[i].gender == Gender::Male &&
(people[i].age >= 25 && people[i].age <= 40)) {
cout << people[i] << "\n";
}
}
Слайд 60Range based for
for (auto& person : people) {
if ((person.gender
== Gender::Female) &&
(person.age >= 20 &&
person.age <= 30)) {
cout << person << "\n";
}
}
for (auto& person : people) {
if ((person.gender == Gender::Male) &&
(person.age >= 25 && person.age <= 40)) {
cout << person << "\n";
}
}
Слайд 61Filtered-range
auto ByGenderAndAge = [](Gender g, int minAge, int maxAge) {
return [=](auto& p) {
return (p.gender == g)
&& p.age >= minAge && p.age <= maxAge;
};
};
auto ToStdout = ostream_iterator
(cout, "\n");
copy(people | filtered(ByGenderAndAge(Gender::Female, 20, 30)), ToStdout);
copy(people | filtered(ByGenderAndAge(Gender::Male, 25, 40)), ToStdout);
Слайд 62Ищем, есть ли женщины за 30 (raw for)
bool hasWomenOlderThan30 =
false;
for (size_t i = 0; i < people.size(); ++i) {
if (people[i].age > 30 && people[i].gender == Gender::Female) {
hasWomenOlderThan30 = true;
break;
}
}
if (hasWomenOlderThan30)
cout << "There are women older than 30\n";
else
cout << "There are no women above 30\n";
Слайд 63Ищем, есть ли женщины за 30 (any_of)
auto olderThan = [](int
age) {
return [=](auto& p) { return p.age > age;
};
};
auto isFemale = [](auto& p) { return p.gender == Gender::Female; };
if (any_of(people | filtered(olderThan(30)), isFemale))
cout << "There are women older than 30\n";
else
cout << "There are no women above 30\n";
Слайд 64Паттерны проектирования в функциональном стиле
Слайд 66Продукт
struct IProduct {
virtual ~IProduct() = default;
virtual void Foo()
= 0;
};
struct ConcreteProduct : IProduct {
ConcreteProduct(int data):m_data(data) {}
void
Foo() override {}
private:
int m_data;
};
Слайд 67struct IFactory {
virtual ~IFactory() = default;
virtual unique_ptr CreateProduct()
= 0;
};
void Client(IFactory& factory) {
auto product = factory.CreateProduct();
product->Foo();
}
struct
ConcreteFactory : IFactory {
ConcreteFactory(int data) :m_data(data) {}
unique_ptr
CreateProduct() override {
return make_unique(m_data);
}
private:
int m_data;
};
void TestClassicFactory() {
ConcreteFactory factory(42);
Client(factory);
}
Интерфейс фабрики
Клиентский код
Полезный код
Тестовый пример
Слайд 68using Factory = function;
void Client(const Factory& makeProduct) {
auto product
= makeProduct();
product->Foo();
}
void TestFunctionalFactory() {
Client([] {return make_unique(42); });
}
Фабрика в
функциональном стиле
Слайд 70enum class WalkDirection { North, South, West, East, };
struct Robot
{
void TurnOn();
void TurnOff();
void Walk(WalkDirection direction);
void Stop();
…
};
Робот
Слайд 71struct ICommand {
virtual ~ICommand() = default;
virtual void Execute()
= 0;
};
struct Menu {
void AddItem(string shortcut, string descr, unique_ptr
&& command);
…
};
struct TurnOnCommand : public ICommand {
TurnOnCommand(CRobot & robot): m_robot(robot) {}
void Execute() override {
m_robot.TurnOn();
}
private:
Robot & m_robot;
};
Robot robot;
Menu menu;
menu.AddItem("on", "Turns the Robot on", make_unique(robot));
Слайд 72struct MenuFP {
typedef std::function Command;
void AddItem(string shortcut, std::string
descr, const Command & command);
…
};
Robot robot;
MenuFP menu;
menu.AddItem("on", "Turns the
Robot on", [&] { robot.TurnOn(); });
Команда в функциональном стиле
Слайд 74Ссылки
Миграция на современный C++ 17
Интервалы с C++
С++ without new and
delete
https://github.com/alexey-malov/CppMeeting-Kazan2017