27:00 Не уверен, но предположу: в случае implicit move нужно куда-то сохранить адрес изначального объекта, для которого в конце вызывать move ctor, чтобы освободить регистры для вызова append (так как компилятор не знает, что append вернет то же самое). А в случае явного move можно его перезатереть, так как возвращать все равно будем то, что вернет append
Огромное спасибо за лекцию, все, как всегда, прекрасно и супер-познавательно! Только в 1:08:00 , кажется, небольшая неточность. У shared_ptr и так есть неявные конструкторы из shared_ptr, так что по сути они совсем ковариантны, как и обычные указатели, а вот static_pointer_cast (ну и dynamic) нужен для преобразования вниз по иерархии. Так что вторая строка может выглядеть так: std::shared_ptr a = b;
52:40 Несколько интересно, что можно хранить указатель никак не связанный с подконтрольным, а также в принципе не обладать каким-либо объектом: struct S {}; S obj; std::shared_ptr s(std::shared_ptr{}, &obj); // хранит адрес obj не владея им И оказывается weak_ptr тоже хранит указатель, сохраняя алиасинг
7:55 Внезапно понял окончательно, почему повсеместно в стандарте в шаблонном коде используется формат direct-initialization против list-initialization Ну подумаешь, отдаётся приоритет list конструкторам, так здесь же объект инициализируется таким же объектом, копирование на лицо. А потом приходит человек с таким классом struct S { S() = default; S(S const &) { std::cout
Не понял, как мы без наследования (EMBCO), через включение добились оптимизации по памяти? 37:20 Как может поле класса занимать 0 байт? Или я вообще неверно понял концепцию, подскажите, пожалуйста.
Не раскрыт момент, почему make_unique не работает с кастомным удалителем 37:58. Технически разве это не сделать? Насколько я понял - концептуальное противоречие с тем, что кастомный удалитель подразумевает кастомное же и создание, а его как раз и не сделать в make_unique по определению.
Я как раз и надеюсь что любой человек попробует это воспроизвести в голове а то и написать свою перегрузку make_unique с удалителем и сам осознает проблему. Там буквально два простых логических шага и вы их уже сделали.
@@tilir мой мысленный эксперимент сходу не привел меня к «тупику» и я, признаюсь, нашел чужой ответ про кастомное создание, и тогда картинка сложилась. То есть получается, кастомный удалитель нужен только тогда, когда мы имеем дело с указателем на что-то «непонятное» (не класс)? Иначе мы бы пользовались конструктором и деструктором класса.
Как вы относитесь к подходу использования сырых указателей, как "невладеющих"? Когда проблема владения и безопасности исключения решена unique_ptr'ом, проблема вызова delete - договоронностью внутри проекта, а вопрос лайфтайма не стоит (например класс-юзер всегда создаётся после класса-владельца, и умирает раньше него)? Часто вижу такое в чужих кодовых базах, где это решения принято "осознанно", а не из-за легаси. В дополнение к предыдущему: Есть ли случаи, когда ссылка на unique_ptr это не "кишки наружу" и не гадость? Ещё видел что люди передают в функции и алиасят shared_ptr'ы через константные ссылки, чтобы попусту не пересчитывать референсы в однопоточном контексте. Что по этому поводу думаете? Что можно почитать на тему непосредственно проектирования с использованием умных указателей, чтобы программа не превращалась в shared_ptr-фест?
Очень часто сырые указатели как невладеющие просто неизбежны. Но обычно их стараются заинкапсулировать чтобы они всё равно наружу не торчали. Ссылка на unique_ptr это из общих соображений сомнительная техника, но если у вас есть конкретные соображения, они всегда кроют соображения общие. Я лично примеров уместного использования unique_ptr& не знаю, но их не может не быть. "Что почитать на тему проектирования " это чаще всего крик в пустоту. Вот и в вашем случае мне такого не попадалось.
36:55 Размер пары почему-то равен 2 байтам, а не 1. Можете пояснить этот момент? #include struct A {}; struct B {}; int main() { A a; B b; using namespace std; pair p; cout
@@tilir Константин, а как вы предполагали это оптимизировать для pair? До С++20 не было [[no_unique_address]], так что кроме как через наследование, когда оно допустимо, разместить объекты по одному адресу не получится. А в pair они должны быть именованы как first/second
@@tilir да, это оно. Перенос интрузивного указателя из Boost в стандарт с некоторой работой над ошибками -- например, вместо free functions предлагаются трейты. Подробнее можно посмотреть в P0468, ссылка в README в репозитории.
1:18:12 а точно ли это прям правильная картинка? разве weak_ptr не должен хранить внутри себя указатель на данные? ведь он же не всегда может его вытащить из контрольного блока, ибо может быть сконструирован от алиасного shared_ptr
Когда я говорю о верной картинке я говорю о том что нарисовал в контрольном блоке всё что там есть. Так-то если расписывать устройство самих указателей, можно ещё много что нарисовать.
27:00
Не уверен, но предположу: в случае implicit move нужно куда-то сохранить адрес изначального объекта, для которого в конце вызывать move ctor, чтобы освободить регистры для вызова append (так как компилятор не знает, что append вернет то же самое). А в случае явного move можно его перезатереть, так как возвращать все равно будем то, что вернет append
Огромное спасибо за лекцию, все, как всегда, прекрасно и супер-познавательно!
Только в 1:08:00 , кажется, небольшая неточность. У shared_ptr и так есть неявные конструкторы из shared_ptr, так что по сути они совсем ковариантны, как и обычные указатели, а вот static_pointer_cast (ну и dynamic) нужен для преобразования вниз по иерархии.
Так что вторая строка может выглядеть так: std::shared_ptr a = b;
Ого, это тонкое наблюдение, спасибо. Я проверю и если что обновлю в errata.
Главнаый мотив не создавать в деструкторе что-либо, требование для создаваемого объекта быть noexept. В остальном, всё должно быть нормально.
52:40 Несколько интересно, что можно хранить указатель никак не связанный с подконтрольным, а также в принципе не обладать каким-либо объектом:
struct S {};
S obj;
std::shared_ptr s(std::shared_ptr{}, &obj); // хранит адрес obj не владея им
И оказывается weak_ptr тоже хранит указатель, сохраняя алиасинг
Интересная техника!
1:23:00
Почему в интрузивном счётчике не нужно его делать атомарным?
1:29:35
За что Херб Саттер ни возьмётся
Всё превращается в ничто
А если он за void берётся
То просто тратит меньше сил.
34:51
Я правильно понял, что здесь имеется ввиду обычное CRTP со специализированным шаблоном?
43:25 std::foo
Здравствуйте, спасибо за лекцию. Возможно, на 1:21:10 имелся в виду shared_ptr как тривиальная cow строка?
Да, уже заметили в комментариях.
7:55 Внезапно понял окончательно, почему повсеместно в стандарте в шаблонном коде используется формат direct-initialization против list-initialization
Ну подумаешь, отдаётся приоритет list конструкторам, так здесь же объект инициализируется таким же объектом, копирование на лицо. А потом приходит человек с таким классом
struct S {
S() = default;
S(S const &) { std::cout
Да, это интересный пример. У меня была лекция про инициализацию в углублённом курсе, надо было бы его туда добавить.
26:52 получается мы возвращяем совершенно разные вещи (объект в первом случае и результат вызова append во втором)
Я же объяснил словами: у нас есть внутреннее знание что append возвращает *this.
Прошу прощения за это, действительно пример не для детей.
Не понял, как мы без наследования (EMBCO), через включение добились оптимизации по памяти? 37:20
Как может поле класса занимать 0 байт? Или я вообще неверно понял концепцию, подскажите, пожалуйста.
Поле и не занимает 0. Оно занимает размер указателя т.к. кортеж из указателя и stateless класса как раз внутри оптимизирован через EBCO.
@@tilir спасибо, понял. Это я что-то вообще неверно разглядел сначала.
Не раскрыт момент, почему make_unique не работает с кастомным удалителем 37:58. Технически разве это не сделать? Насколько я понял - концептуальное противоречие с тем, что кастомный удалитель подразумевает кастомное же и создание, а его как раз и не сделать в make_unique по определению.
Я как раз и надеюсь что любой человек попробует это воспроизвести в голове а то и написать свою перегрузку make_unique с удалителем и сам осознает проблему. Там буквально два простых логических шага и вы их уже сделали.
@@tilir мой мысленный эксперимент сходу не привел меня к «тупику» и я, признаюсь, нашел чужой ответ про кастомное создание, и тогда картинка сложилась. То есть получается, кастомный удалитель нужен только тогда, когда мы имеем дело с указателем на что-то «непонятное» (не класс)? Иначе мы бы пользовались конструктором и деструктором класса.
Как вы относитесь к подходу использования сырых указателей, как "невладеющих"? Когда проблема владения и безопасности исключения решена unique_ptr'ом, проблема вызова delete - договоронностью внутри проекта, а вопрос лайфтайма не стоит (например класс-юзер всегда создаётся после класса-владельца, и умирает раньше него)? Часто вижу такое в чужих кодовых базах, где это решения принято "осознанно", а не из-за легаси.
В дополнение к предыдущему: Есть ли случаи, когда ссылка на unique_ptr это не "кишки наружу" и не гадость?
Ещё видел что люди передают в функции и алиасят shared_ptr'ы через константные ссылки, чтобы попусту не пересчитывать референсы в однопоточном контексте. Что по этому поводу думаете?
Что можно почитать на тему непосредственно проектирования с использованием умных указателей, чтобы программа не превращалась в shared_ptr-фест?
Очень часто сырые указатели как невладеющие просто неизбежны. Но обычно их стараются заинкапсулировать чтобы они всё равно наружу не торчали.
Ссылка на unique_ptr это из общих соображений сомнительная техника, но если у вас есть конкретные соображения, они всегда кроют соображения общие. Я лично примеров уместного использования unique_ptr& не знаю, но их не может не быть.
"Что почитать на тему проектирования " это чаще всего крик в пустоту. Вот и в вашем случае мне такого не попадалось.
36:55 Размер пары почему-то равен 2 байтам, а не 1. Можете пояснить этот момент?
#include
struct A {};
struct B {};
int main()
{
A a;
B b;
using namespace std;
pair p;
cout
Это интересное наблюдение. Действительно для std::tuple работает, для std::pair нет, причём и libstdc++ и libc++
std::pair p;
std::tuple t;
std::cout
@@tilir Константин, а как вы предполагали это оптимизировать для pair? До С++20 не было [[no_unique_address]], так что кроме как через наследование, когда оно допустимо, разместить объекты по одному адресу не получится. А в pair они должны быть именованы как first/second
В стандарт предлагали std::retain_ptr. Впрочем, не взлетело, и автор в 2021 году все это дело забросил.
Об этом не слышал. Как я понимаю вот это: github.com/bruxisma/retain-ptr
А в чём если коротко была идея?
@@tilir да, это оно. Перенос интрузивного указателя из Boost в стандарт с некоторой работой над ошибками -- например, вместо free functions предлагаются трейты. Подробнее можно посмотреть в P0468, ссылка в README в репозитории.
@@tilir и да, спасибо за отличную лекцию :)
По моему на трансляции эха не было...
Это не эхо, это по задумке стереозвук =)
1:20:41 кажется, у вас тут опечатка на слайде. разве не shared_ptr?
Да, совершенно точно, спасибо. Это явно в errata.
1:18:12 а точно ли это прям правильная картинка? разве weak_ptr не должен хранить внутри себя указатель на данные? ведь он же не всегда может его вытащить из контрольного блока, ибо может быть сконструирован от алиасного shared_ptr
Когда я говорю о верной картинке я говорю о том что нарисовал в контрольном блоке всё что там есть. Так-то если расписывать устройство самих указателей, можно ещё много что нарисовать.