Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
 
 
Рейтинг 4.95/173: Рейтинг темы: голосов - 173, средняя оценка - 4.95
Jesus loves me
Эксперт С++
5167 / 3139 / 353
Регистрация: 12.12.2009
Сообщений: 7,930
Записей в блоге: 2
1

С++ идиомы

31.07.2016, 19:20. Показов 34376. Ответов 34
Метки нет (Все метки)

Перевод статей 1 и 2. Будет постепенно обновляться. Желающие внести вклад могут писать в ЛС.

 Комментарий модератора 
Тема открыта, просьба добавлять только посты с переводом, обсуждение здесь


Переведенные идиомы:
self-assignment in an assignment operator
Scope Guard
Shrink-to-fit
Checked delete
Pointer To Implementation
Получение адреса(ака взятие адреса aka Address-of)
nullptr
Iterator Pair
Coercion by Member Template
14
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
31.07.2016, 19:20
Ответы с готовыми решениями:

С++ идиомы - обсуждение
Тема создана для вопросов и обсуждений С++ идиом

Как и какие идиомы и паттерны можно (и лучше) применять?
Здравствуйте. Писал я проги для себя ну и так по мелочи, для абы кого, да и проги абы как, не...

Идиомы программирования
диома программирования — это некоторое часто применяемое действие в прораммировании. Это самый...

Английские идиомы: как правильно перевести in its own right?
Как правильно перевести in its own right? Например, вот в таком контексте: Добавлено через 3...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
34
фрилансер
2498 / 1905 / 473
Регистрация: 11.10.2019
Сообщений: 5,604
14.03.2021, 21:21 21
JeyCi,

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(int argc, const char *argv[])
{
    int a=2;
    {
       vector<unique_ptr<AbstractShape>>  v;
       v.emplace_back(new Square(a));
       v.emplace_back(new Circle(a));
       v.emplace_back(new Rectangle(a, 4));
            
        for(const auto& el : v)
        {
            cout << el->area() << '\n'; 
        }
    }
    return 0;
}
1
14452 / 7839 / 1880
Регистрация: 30.01.2014
Сообщений: 13,240
16.03.2021, 14:56 22
Цитата Сообщение от JeyCi Посмотреть сообщение
часто CRTP описывается только с template'ом и наследниками
Потому что именно это в его названии и заложено.

Вообще я убежден, что CRTP должен быть элементом дизайна, а не оптимизации.
Нет никакого смысла вводить его только ради оптимизации. Должны быть более фундаментальные причины.
1
14452 / 7839 / 1880
Регистрация: 30.01.2014
Сообщений: 13,240
16.03.2021, 15:44 23
JeyCi, я вам тут прикрепил pdf-ку со статьей из журнала C++ Report от 95 года по CRTP. Думаю полезно будет ознакомиться для полного понимания.
2
Вложения
Тип файла: pdf The-C-Report (CRTP).pdf (854.5 Кб, 18 просмотров)
Любитель чаепитий
3560 / 1666 / 513
Регистрация: 24.08.2014
Сообщений: 5,640
Записей в блоге: 1
16.03.2021, 16:36 24
Цитата Сообщение от Алексей1153 Посмотреть сообщение
да, я слегка ошибся - у тебя запутанная архитектура ) Утечки не будет

Не по теме:

Кликните здесь для просмотра всего текста
Код
anon@DESKTOP:/mnt/d/Projects/tmp$ valgrind --leak-check=full ./a.out
==75== Memcheck, a memory error detector
==75== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==75== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==75== Command: ./a.out
==75==
==75== error calling PR_SET_PTRACER, vgdb might block
4
12.56
8
==75==
==75== HEAP SUMMARY:
==75==     in use at exit: 56 bytes in 3 blocks
==75==   total heap usage: 11 allocs, 8 frees, 73,384 bytes allocated
==75==
==75== 16 bytes in 1 blocks are definitely lost in loss record 1 of 3
==75==    at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==75==    by 0x109189: main (crtp_leak.cpp:63)
==75==
==75== 16 bytes in 1 blocks are definitely lost in loss record 2 of 3
==75==    at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==75==    by 0x1091A7: main (crtp_leak.cpp:64)
==75==
==75== 24 bytes in 1 blocks are definitely lost in loss record 3 of 3
==75==    at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==75==    by 0x1091C5: main (crtp_leak.cpp:65)
==75==
==75== LEAK SUMMARY:
==75==    definitely lost: 56 bytes in 3 blocks
==75==    indirectly lost: 0 bytes in 0 blocks
==75==      possibly lost: 0 bytes in 0 blocks
==75==    still reachable: 0 bytes in 0 blocks
==75==         suppressed: 0 bytes in 0 blocks
==75==
==75== For counts of detected and suppressed errors, rerun with: -v
==75== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

1
Заблокирован
16.03.2021, 20:20 25
DrOffset, спасибо... начинаю понимать, как создаётся язык (что и есть цель ООП по сравнению с процедурным пр-ем), паттерны и Finite State Machine ... - прямо откровения от автора ... интересные... особенно понравилась фраза
baseFSM class provides a framework for programmer
я тогда посоветую ещё - Федор Г. Пикус | Идиомы и паттерны проектирования в современном С++ (2020) [PDF] - может здесь брала... стр 274-276 хорошо описаны ограничения CRTP классического и расширенного основным класслм (как описано в вашей pdf и как я пыталась кодом выше) -
Pros:
1) шаблон генерит функции для потомков авоматически, чтобы их не писать во всех потомках - в классическом варианте
2) по расширенной версии с базовым классом... использование перекрёстного приведения (своеобразный dynamic-cast)
Если во время выполнения выяснится, что фактический производный объект имеет тип, не являющийся комбинацией обоих базовых классов, то dynamic_cast завершится неудачно и вернёт NULL (или возбудит исключение, если мы используем не указатели, а ссылки)
- "обоих базовых классов" - это базового и template'ового... в вашей pdf в принципе, о том тоже указано...
вообще, интересный трюк - наследоваться от базового и от template'ового... - наверно, даже таким способом и страховку от "алмаза смерти" можно изобрести?... не пробовала ещё...
только вот получить наследника второй очереди - с этим наследуемым виртуальным clone(), задаваемым в шаблоне, чтобы не прописывать его во всех наследниках, - уже вроде не получится ....

Добавлено через 6 минут

P.S.
Кликните здесь для просмотра всего текста
Цитата Сообщение от GbaLog- Посмотреть сообщение
==75== 16 bytes in 1 blocks are definitely lost in loss record 1 of 3
==75== at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==75== by 0x109189: main (crtp_leak.cpp:63)
==75==
==75== 16 bytes in 1 blocks are definitely lost in loss record 2 of 3
==75== at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==75== by 0x1091A7: main (crtp_leak.cpp:64)
==75==
==75== 24 bytes in 1 blocks are definitely lost in loss record 3 of 3
==75== at 0x4C3217F: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==75== by 0x1091C5: main (crtp_leak.cpp:65)

значит new от строк 63-65 надо потом delete.. т.е. они, вероятно, не от-move-ились в unique_ptr, а, наверно, от-copy-лись... простите за выражения
0
GbaLog-
17.03.2021, 07:09
  #26

Не по теме:

Цитата Сообщение от JeyCi Посмотреть сообщение
т.е. они, вероятно, не от-move-ились в unique_ptr
а что и куда у вас должно "отмувнуться"? не вижу такого когда у вас.

0
JeyCi
17.03.2021, 07:31
  #27

Не по теме:


Цитата Сообщение от JeyCi Посмотреть сообщение
Shape(Shape&&) = default; // ? конструктор перемещения
//====
std::unique_ptr<AbstractShape> // родительский ptr в unique заворачивается...
вообще, действительно, как-то не прозрачно... но тема конструкторов, наверно, уже др. тема... или делать явный std::move ...
спасибо, что прогнали через Valgrind

0
Алексей1153
17.03.2021, 07:31
  #28

Не по теме:

JeyCi, утечка там вот тут:

Код
std::unique_ptr<AbstractShape> clone() const override
{
     return std::make_unique<Derived> //создаётся НОВЫЙ объект обёртки
        (static_cast<Derived const&>(*this));//в конструктор передаётся КОПИЯ *this - снаружи this не удалится сам, так как создан new
}

0
Заблокирован
18.03.2021, 08:57 29
Цитата Сообщение от DrOffset Посмотреть сообщение
CRTP должен быть элементом дизайна, а не оптимизации.
Нет никакого смысла вводить его только ради оптимизации. Должны быть более фундаментальные причины.
? вижу только одну возможную фундаментальную причину - не дублировать код в потомках (чтобы было легче сопровождать) и - вторую - не отдавать полиморфизм на откуп run-time'у (для скорости)... имхо
p.s.
для меня могут быть только 4 фундаментальные причины:
- для увеличения скорости,
- для минимизации памяти,
- для минимизации писанины,
- для того, чтобы не потерять что-нибудь (что приводит к memory leaks)...
разве есть ещё какие-то более фундаментальные причины, например для использования CRTP?..
0
14452 / 7839 / 1880
Регистрация: 30.01.2014
Сообщений: 13,240
18.03.2021, 09:16 30
Цитата Сообщение от JeyCi Посмотреть сообщение
для увеличения скорости,
Обычно там, где действительно нужен runtime-полиморфизм, никак не получится его заменить на compile-time.

Цитата Сообщение от JeyCi Посмотреть сообщение
разве есть ещё какие-то более фундаментальные причины
Я говорил только про выбор CRTP в качестве решения для замены runtime-полиморфизма.
И суть моих слов в том, что дизайн должен быть основным критерием, а не производительность. В смысле, что производительность - это следствие, а не первопричина.
0
Заблокирован
18.03.2021, 13:07 31
Цитата Сообщение от DrOffset Посмотреть сообщение
CRTP должен быть элементом дизайна, а не оптимизации
ну в общем, да, - если по логике нужны будут разные реализации от одного интерфейса... то CRTP расширенный очень даже хорош (хоть некоторые и пеняют на "читаемость и отлаживаемость кода" по сравнению с использованием обычных Абстрактных классов )...
Единственная ситуация, когда вам действительно нужно использовать динамический полиморфизм, — это когда реализация недоступна во время компиляции; например, когда он загружен из динамической библиотеки.
- вот я пока останусь при такой точке зрения...
спасибо за view: performance <-design-> optimization (& 1<->3)
0
Avazart
18.03.2021, 15:45
  #32

Не по теме:

Да хорош тему засерать и тролиху кормить.

0
Заблокирован
23.03.2021, 18:40 33
Цитата Сообщение от Алексей1153 Посмотреть сообщение
Зачем метод clone?
вот читаю книгу из #25 - так речь о CRTP-фабрике в главе 13 - там и примеры с clone()... а сам CRTP в главе 8 описывается... а wiki, наверно, вырвали кусок и этот clone() не заменили на любую более вразумительную функцию...
только сейчас поняла, почему clone() - рудимент от фабрики - может быть любая др. функция...
0
Заблокирован
18.05.2021, 19:55 34
да, с CRTP-фабрикой, - примером с wiki, - я всё-таки поспешила...
Цитата Сообщение от DrOffset Посмотреть сообщение
CRTP должен быть элементом дизайна, а не оптимизации.
Нет никакого смысла вводить его только ради оптимизации. Должны быть более фундаментальные причины.
всё-таки смысл статического полиморфизма (в данном случае) - Lazy: CRTP...
That is possible because the method will be instantiated when called.
или здесь...
===
- полагаю, желание/потребность подрядить ленивые вычисления/методы и может стать фундаментальным аргументом в пользу выбора CRTP (иных пока не вижу)...
только я пока не выбирала ленивые методы... как бы не вижу в них преимуществ... разве что по памяти (чтобы функции-члены не инстанцировались в случае, если вдруг потом могут и не понадобится)... имхо

Добавлено через 4 минуты
p.s.
вероятно, для подмешивания этот трюк в основном используется... так же
MixIn функциональность не обязательно должна быть включена внутрь некоторого класса. Иногда возможно ее реализовать в виде свободной функции
0
Заблокирован
19.05.2021, 10:42 35
MixIn функциональность не обязательно должна быть включена внутрь некоторого класса. Иногда возможно ее реализовать в виде свободной функции
хотя, как справедливо подмечено, - просто ради удобства лучше инкапсулировать
With the CRTP, you can see that Sensitivity offers the interface of NumericalFunctions...
And with the template non-member functions you don’t. They would be hidden behind a #include somewhere.
===
Цитата Сообщение от JeyCi Посмотреть сообщение
для подмешивания
оставлю работающий код (с линка - просто там нарезки) для ситуации подмешивания нескольких Базовых классов
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// https://habr.com/ru/post/210894/
 
// Возможно, вы заметили в предыдущих главах “Явный интерфейс” и “MixIn наоборот” я использовал открытые функции в производном классе. Вообще говоря, это не очень хорошо, так как нарушает инкапсуляцию. Получается, что наружу у нас “торчат” функции, которые не предназначены для того, что пользователь вызывал их напрямую. 
//Можно решить эту проблему, сделав базовые классы друзьями производного. После этого можно вносить эти функции в private секцию, но представьте, что вам нужно отнаследовать от нескольких базовых MixIn’ов. Придется делать друзьями все базовые классы. Для комплексного решения этой проблемы, а также для того, чтобы обеспечить компиляцию на некоторых старых компиляторах, можно ввести новый уровень косвенности. Он представляет из себя структуру, функции которой перенаправляют вызовы из базы в производный класс.
 
#include <iostream>
 
using namespace std;
 
//  можно ввести новый уровень косвенности. Он представляет из себя структуру, функции которой перенаправляют вызовы из базы в производный класс.
// indirection level
struct access
{
    template<typename Impl>
    static void on_handle_connect(Impl* impl) {impl->handle_connect();}
 
    template<typename Impl>
    static void on_handle_response(Impl* impl) {impl->handle_response();}
};
 
// Теперь из базовых классов, мы вызываем не функции производного, а функции промежуточной структуры.
// BASE
template<typename D>
struct connection_handler
{
    // ...
    void on_connection()
    {
        access::on_handle_connect(static_cast<D*>(this));
    }
};
 
template<typename D>
struct response_handler
{
    // ...
    void on_response()
    {
        access::on_handle_response(static_cast<D*>(this));
    }
};
 
// В производном же классе нам достаточно внести в друзья только структуру access.
// DERIVED
class combined_handler : public connection_handler<combined_handler>, 
public response_handler<combined_handler>
{
    private:
        friend struct access;
        void handle_connect(){ std::cout << "handle_connect: " << __PRETTY_FUNCTION__ << std::endl; }
        void handle_response(){ std::cout << "handle_response: " << __PRETTY_FUNCTION__ << std::endl; }
};
 
 
int main()
{
    combined_handler s1;
 
    s1.on_connection();
    s1.on_response();
   
}
/*
handle_connect: void combined_handler::handle_connect()
handle_response: void combined_handler::handle_response()
*/

- всё-таки доп. уровень косвенности в CRTP через друзей может помочь и избежать "Алмаза смерти" (подробнее здесь - смысл friend в подмешивании)...
===
и так же (по последнему линку) - это попытка избавиться от большого количества static_cast'ов при реализации CRTP-паттерна, которая как раз и заключается в выносе всех static_cast'ов на верх. уровень иерархии, а чтобы оттуда не получить ромбовидного наследования - использование template'а, принимающего параметром тоже template (а не конкретный type)
Note that the template parameter is not just a typename, but rather a template<typename> class. This simply means that the parameter is not just a type, bu rather a template itself, templated over a type whose name is omitted. For example crtpType can be
===
в общем, CRTP, конечно, вариант, когда все типы известны на этапе компиляции, - для статического полиморфизма... но всё-таки глубокие иерархии всё равно не стоит делать... а диспетчеризация через visit и variant в std даёт нужное и без иерархий... появление std::variant -- всё-таки большой шаг вперёд был в std... имхо
0
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.