TGStat
TGStat
Type to search
Advanced channel search
  • flag English
    Site language
    flag Russian flag English flag Uzbek
  • Sign In
  • Catalog
    Channels and groups catalog Regional compilations Thematic compilations Платные каналы Search for channels
    Add a channel/group
  • Ratings
    Rating of channels Rating of groups Posts rating
    Ratings of brands and people
  • Analytics
  • Search by posts
  • Telegram monitoring
  • Promotion
    Advertising through Yandex Business Advertising in channels through TGStat Agency Advertising on TGStat.ru website
this->notes.

24 Sep, 09:23

Open in Telegram Share Report

#cpp

Представьте вот такой код:

auto object = GetObject(params...);

auto another = object;
MessAround(object);

// (1) you want to write some logic here

Где MessAround — функция, которая непредсказуемым образом меняет значение вашего object. То есть значение соответствует всем инвариантам, но вы не знаете, какое конкретно.

Как вы будете писать код в (1)?

Вы наверное проверите какие-то свойства вашего object. Может он там empty()/isNull()/valid(). Или может вы сразу решите его почистить: clear(). Или может присвоить что-нибудь туда захотите: object = Object{1, 2, 3}.
Или можете вообще сравнить его с чем-то (вдруг там всё-таки какое-то конкретное значение появилось):

if (object == "str_val"_obj) {...}


Делаете ли вы что-то неправильное? Должен ли компилятор вам подсказать проблему в таком коде? Может UBsan? Может clang-tidy?

Да нет.

Более того, даже если вы сделаете что-то такое:

auto first = object["first"];

Вы возможно ничего плохого не сделали. Вы просто не знаете, что точно там лежит. MessAround туда могло подложить что угодно.

Конечно, проблемы могут возникнуть, если вы, не проверив состояние вашего объекта, будете предполагать, что он обладает какими-то свойствами. Например, думать, что он не пустой. Или что он содержит сколько-то каких-то элементов. Это всё может быть, но вообще-то мы точно утверждать не можем.

Использовать объект после MessAround это примерно то же самое, что написать функцию, решающую некоторую задачу в вакууме. На примере функции, решающей любую задачу:

void solve(Object obj) {...}

Вы опять же про этот obj ничего не знаете. Вам надо проверить разные корнер кейсы. А потом как-то там решать вашу проблему.

Чем эти примеры отличаются от работы с переменной после std::move?

Да ничем.

Мы так яро привыкли к правилу «использовать переменную после std::move опасно», что в нашей ментальной модели это часто равносильно undefined behaviour. Уже само это действие вызывает подозрения. Code smell так называемый.

Хотя никакого UB тут нет. UB может возникнуть от того, что вы пользуетесь объектом, предполагая, что у него есть какие-то свойства. Точно так же и в нашей функции solve, которая может не учесть какие-то потенциальные входные данные. Пытаться разыменовать пустой указатель, не проверив его, неправильно.

И к сожалению эта ошибка стала достаточно частой, чтобы мы выработали ощущение неправильности происходящего. Само действие стало табу. В clang-tidy вон проверка отдельная есть (которую приходится игнорировать с NOLINT).

Отсюда рождаются ментальные модели вида
• «после std::move ничего нельзя»
• «после std::move можно только переприсваивать или уничтожать объект, всё остальное запрещено».
Это упрощения, которые мы себе придумали, чтобы меньше думать. И они почти всегда работают. Но иногда всё же нет.

Давайте думать и не вестись на вот эти попытки наших развитых эволюционировавших мозгов упрощать. Это мы профессионально ленимся.

P.S. Важно подчеркнуть ещё один факт.
Состояние объекта после std::move — всем известное «valid but unspecified». По стандарту это работает для стандартной библиотеки. Но кажется, мы уже настолько к этому привыкли, что считаем это данностью в любом случае. Не то чтобы это ожидание обосновано. Авторы библиотек делают что хотят. Но наверное писать ломающий это ожидание код всё-таки не надо.
А для стандартной библиотеки довольно просто понять, можно ли вызывать метод у мувнутого объекта: у метода не должно быть precondition. Например у std::vector::front он есть: !empty(); у std::vector::clear такого нет.

@thisnotes. Patreon, newsletter.
Спасибо Artyom Garkavy и niki4smirn.

1.9k 0 5 16 17
Catalog
Channels and groups catalog Channels compilations Search for channels Add a channel/group
Ratings
Rating of Telegram channels Rating of Telegram groups Posts rating Ratings of brands and people
API
API statistics Search API of posts API Callback
Our channels
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Read
Академия TGStat Telegram Research 2019 Telegram Research 2021 Telegram Research 2023
Contacts
Справочный центр Support Email Jobs
Miscellaneous
Terms and conditions Privacy policy Public offer
Our bots
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot