Слайд 1Современный C++
5 крутых фишек
Алексей Малов
iSpring Solutions
www.ispringsolutions.com
Слайд 2Содержание
Семантика перемещения
Умные указатели
Optional
Variant
Контейнеры, диапазоны, алгоритмы
Применение функционального программирования
Слайд 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
Копирование сотрудников при изменерии размеров контейнера
Слайд 6vector
Happy
new
year!
vector
H
a
p
p
y
n
e
w
Что происходит при копировании
y
e
a
r
H
a
p
p
y
n
e
w
y
e
a
r
Happy
new
year!
Слайд 7vector
Happy
new
year!
vector
H
a
p
p
y
n
e
w
Что происходит при перемещении
y
e
a
r
Слайд 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
Слайд 9Move-only типы
Для них операция копирования не имеет смысла
Полезно иметь возможность
перемещения значения от одного объекта
Возврат результата из функции
Передача аргумента в
функцию
Пример:
Объекты типа Handle, управляющие некоторыми ресурсами ОС
Слайд 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
Слайд 11 ~Handle() {
if (m_handle != INVALID) {
CloseHandle(m_handle);
}
}
explicit operator bool() const {
return m_handle != INVALID; }
operator HANDLE() const { return m_handle; }
void Close() {
if (!CloseHandle(m_handle)) {
m_handle = INVALID;
throw std::runtime_error("Failed to close handle");
}
m_handle = INVALID;
}
};
} // namespace detail
using FileHandle = detail::Handle;
Слайд 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";
}
Пример использования
Слайд 14unique_ptr
Единолично владеет объектом через его указатель
Move-only
Нулевой оверхед + RAII
Совместим с
stl-контейнерами
Позволяет управлять массивом объектов в куче
Поддержка пользовательского deleter-а
R.I.P. auto_ptr
Слайд 15Идиома Pimpl (pointer to implementation)
Вместо хранения данных, класс хранит указатель
на структуру или класс с деталями реализации
Скрывает лишние зависимости из
заголовочного файла
Позволяет сократить время компиляции
Слайд 16Пример – идиома Pimpl
#include "OpaqueObj.h"
struct OpaqueObj::Impl {
Impl(int data) :
m_data(data) {}
void Foo() { /* do something */ }
int m_data = 42;
};
OpaqueObj::OpaqueObj(int data)
: m_impl(new Impl(data)) {}
OpaqueObj::~OpaqueObj() { delete m_impl;}
void OpaqueObj::Foo() {
m_impl->Foo();
}
OpaqueObj.cpp
class OpaqueObj {
public:
OpaqueObj(int data);
~OpaqueObj();
void Foo();
private:
struct Impl;
Impl* m_impl;
};
OpaqueObj.h
Проблема – требуется реализовать или запретить конструктор копирования и оператор присваивания
Слайд 17Идиома Pimpl с unique_ptr
#include "OpaqueObj.h"
struct OpaqueObj::Impl {
Impl(int data) :
m_data(data) {}
void Foo() { /* do something */ }
int m_data = 42;
};
OpaqueObj::OpaqueObj(int data)
: m_impl(make_unique
(data)){}
OpaqueObj::~OpaqueObj() = default;
void OpaqueObj::Foo() {
m_impl->Foo();
}
OpaqueObj.cpp
class OpaqueObj {
public:
OpaqueObj(int data);
~OpaqueObj();
void Foo();
private:
struct Impl;
unique_ptr m_impl;
};
OpaqueObj.h
Приятный бонус: OpaqueObj из коробки стал Move-only типом
Слайд 18Умное управление ресурсами библиотеки языка C
typedef struct tagData {
int
value;
} Data;
Data* AllocateData();
void DoSomethingWithData(Data *data, int x);
void DeallocateData(Data* data);
Слайд 19int CalculateX(int value);
bool Foo() {
Data *p = AllocateData();
if
(!p) {
cout
return false;
}
if (p->value == 42) {
DeallocateData(p);
return true;
}
DoSomethingWithData(p, CalculateX(p->value));
DeallocateData(p);
return false;
}
Код по-прежнему содержит проблему
Слайд 20int CalculateX(int value) {
if (value < 0) throw std::invalid_argument("Invalid
argument");
return value + 1;
}
bool Foo() {
Data *p =
AllocateData();
if (!p) {
cout << "Failed to allocated data\n";
return false;
}
if (p->value == 42) {
DeallocateData(p);
return true;
}
DoSomethingWithData(p, CalculateX(p->value));
DeallocateData(p);
return false;
}
Если CalculateX выбросит исключение, DeallocateData вызван не будет
Слайд 21struct DataDeleter {
void operator()(Data *data) const noexcept {
DeallocateData(data);
}
};
using DataPtr = std::unique_ptr;
DataPtr SafeAllocateData() {
if (DataPtr
p{AllocateData()})
return p;
throw std::runtime_error("Failed to allocate data");
}
Пользовательский deleter
Слайд 22bool Foo() {
Data *p = AllocateData();
if (!p) {
cout
}
if (p->value == 42) {
DeallocateData(p);
return true;
}
DoSomethingWithData(p, CalculateX(p->value));
DeallocateData(p);
return false;
}
Было
Слайд 23bool Foo() {
try {
auto p = SafeAllocateData();
if (p->value == 42)
return true;
DoSomethingWithData(p.get(), CalculateX(p->value));
} catch (std::exception const &e) {
cout << e.what() << "\n";
}
return false;
}
Стало
Слайд 24shared_ptr
Умный указатель, основанный на подсчете ссылок
Обеспечивает совместное владение объектом
Возможность управления
не только памятью
Накладные расходы
Память для хранения счетчиков
Thread-safe подсчет ссылок
Слайд 25weak_ptr
Слабая ссылка на объект
Не влияет на время жизни
Автоматически обнуляется после
удаления объекта
Пока объект жив, позволяет получить shared_ptr на него
Решает проблему
циклических ссылок и висячих указателей
Master
Slave
Слайд 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
Защита от преждевременного удаления при вызове внешнего кода
Слайд 27using NodePtr = shared_ptr;
struct Node : enable_shared_from_this {
void
AddChild(const NodePtr& node) {
auto self = shared_from_this();
if (node == self || IsDescendantOf(node))
throw invalid_argument("Can't add self or ancestor");
if (m_children.emplace(node).second) {
if (auto oldParent = node->GetParent()) oldParent->m_children.erase(node);
node->m_parent = self;
}
}
NodePtr GetParent()const { return m_parent.lock(); }
bool IsDescendantOf(const NodePtr& node)const {
auto self = shared_from_this();
for (auto x = node->GetParent(); x; x = x->GetParent())
if (x == self) return true;
return false;
}
private:
weak_ptr m_parent;
unordered_set m_children;
};
Слайд 28struct Image { /* some image data */ };
using Callback
= function;
void LoadImageAsync(string_view url, Callback callback);
struct ImageView
{
void ShowImageAtURL(string_view url) {
/* Загрузить изображение асинхронно и вызвать OnImageLoaded */
}
private:
void OnImageLoaded(string_view url, unique_ptr
image);
};
???
Задача: асинхронная загрузка изображения в GUI-приложении
Слайд 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
Слайд 30Идиома weak this
struct ImageView : enable_shared_from_this
{
static auto Create() {
return shared_ptr(new ImageView);
}
void ShowImageAtURL (string_view url) {
weak_ptr
weakSelf = shared_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);
};
А если нельзя владеть ImageView через shared_ptr?
Pimpl
Слайд 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 с соответствующими затратами на копирование
Слайд 34template
struct UniversalPtr
{
constexpr UniversalPtr() noexcept = default;
constexpr
UniversalPtr(nullptr_t) noexcept {}
template UniversalPtr(U& r);
template
U> UniversalPtr(U* p);
template
UniversalPtr(std::unique_ptr&& p);
template UniversalPtr(const std::shared_ptr& p) noexcept;
explicit operator bool()const noexcept;
T& operator*()const noexcept;
T* operator->()const noexcept;
T* get()const noexcept;
private:
static constexpr void NoDelete(T*) noexcept {}
std::shared_ptr m_ptr;
};
Полный фрагмент кода тут: https://goo.gl/yFlql5
Слайд 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