Снятие, увы, подразумевает ручной анализ всего графа вызовов, т.к. если вы снимете не везде, то получится не compile time error, a terminate в рантайме.
Почему? Только если вы продолжаете навешивать noexcept выше по стеку как в вашем примере ниже.
Скорее «люди увидят noexcept и пометят свой код, вызывающий ваш код, тоже как noexcept (т.к. они читали хабр и знают, что это хорошо), а когда в один прекрасный день ваш код перестанет быть noexcept и из него прилетит исключение — всё просто накроется медным тазом.
Это валидный поинт и так делать надо далеко не всегда — если функция достаточно сложная или использует другие функции, о содержимом которых нельзя делать предположения (например, внешняя библиотека), то noexcept выше по стеку навешивать не надо. Никто и не предлагает, вообще-то.
По сути, разговор идет о значениях по умолчанию — по дефолту функцию надо делать constexpr noexcept const (ничего не забыл?) если имплементация позволяет.
Выше по стеку это не предлагается распространять, особенно если у вас там начинается бизнес-логика.
Проблема как раз в том, что из-за вышеназванной аргументации noexcept не пишут вообще нигде, даже в тех тривиальных случаях, когда имплементация метода не позволяет что-либо выбросить никогда в будущем (скажем, to_string от енума, возвращающий std::string_view от данных в RO секции).
Ну так это аргумент противоположного лагеря, лагеря «не ставить везде noexcept».
Основной посыл в том, что нетровающих методов достаточно много, может даже больше, чем тровающих. С++ вообще славится неправильными дефолтами — надо не пинать людей которые explicit у конструкторов не пишут, надо было делать их explicit по дефолту.
К примеру, если у вас геттер возращает std::string_view (или const-ref), то он, скорее всего, noexcept. Даже если вы захотите потом возвращать std::string как-то хитро посчитанный, сигнатура метода (и бинарная совместимость, если она вас заботит) уже будет сломана и наличие noexcept тут врядли будет самой большой проблемой.
Если же вы не разрабатываете библиотеку и вам не важны такие вещи, то пишите noexcept везде — потом можно снять и просто пересобрать код. Шанс того, что кто-то будет делать оптимизации наподобие тех что делает std::vector, проверяя is_nothrow_move_constructible для вашего геттера/алгоритма, исчезающе мал.
Ну типа да, может быть ситуация когда люди увидят noexcept и вместо unique_ptr где-нибудь заюзают delete (ведь так быстрее! и ислючения не тровают, вот же noexcept!) и потом, когда noexcept уберут, и в этом коде пролетит исключение, объект утечет… Ну так не надо заниматься premature optimisation и закладываться на это, пока профайлер не скажет что «вот это RAII тут тормозит».
Хз что там в «нормальном» языке, но использование volatile для многопоточки — вещь, за которую надо увольнять. Да, volatile защищает от оптимизаций компилятора, но он не делает UB код меньшим UB, просто заметает баги под ковер. Ровно до тех пор пока вы не запустите свой «офигенный» «быстрый» алгоритм на volatile и reinterpret_cast'ах на каком-нибудь ARM.
К сожалению, x86 и друзья являются «strong ordered» архитектурами и поймать instruction reorder там сложно (вот хорошая статья, демонстрирующая, как его всё-таки словить), а значит ваш супер-мега «локфри» на волатайлах будет успешно «работать», возможно в продакшне и не один год.
Во-вторых, значение status::up может «застрять» в кеше процессора, из-за чего цикл сделает кучу холостых операций.
Это, кстати, распространённый миф, кэши процессора когерентны и если вы в одну кэш линию что-то записали, то она «разъедется» на все ядра.
Другое дело что никто не гарантирует что запись будет осуществлена в том месте где вы написали (процессор/компилятор вольны переставлять инструкции) или вообще осуществлена — никто же не сказал что это атомик, можно вообще запись в кэш/память выкинуть или на регистрах оставить.
Нельзя менять/читать значение неатомарной переменной из разных потоков. Если компилятор сможет доказать что в этом потоке переменная не меняется, он может выкинуть проверку нафиг или считать значение из памяти один раз, заменив проверку в цикле на if перед бесконечным циклом.
Я видел достаточно проектов, внедряющих PCH, и почти везде звучало вот это «ой, скрытые зависимости, давайте сделаем сборку без PCH и будем иногда её запускать».
Я видел достаточное количество проектов где внедрили pch, и подсветка в половине хедеров была сломана потому что «stdafx.h» включается до всех остальных хедеров.
Начинать стоит с ccache/clcache (или что там сейчас вместо него), так как прирост производетельности огромный по сравнению с PCH. В случае CI на этом можно и остановиться, так как PCH любят «прятать» забытые инклюды.
На машинах разработчиков можно и PCH настроить для пущей скорости.
Я не очень умею выражать свои мысли, поэтому давайте пример=)
setAutoDelete(false) нужен для того, чтобы переиспользовать раннаблы несколько раз. Например, мы хотим запускать раннабл дважды:
auto tp = QThreadPool::globalInstance();
auto run = new Runnable(); // класс наследует QRunnable и что-то исполняет
run->setAutoDelete(false); // хотим переиспользовать
tp->start(run); // запустили первый раз
tp->start(run); // запустили второй раз
run->setAutoDelete(true);
/* а вот так^ делать нельзя, потому что пул мог уже
обработать раннаблы и не успеть прочитать булку. Это поведение
описано в документации, "пожалуйста, не стреляйте себе в ногу"*/
Теперь тот же юзкейз на shared_ptr:
auto tp = QThreadPool::globalInstance();
auto run = std::make_shared<Runnable>();
tp->start(run); // запустили первый раз
tp->start(run); // запустили второй раз
Фишка в том, что мы получили тот же юзкейз и нам эта булка вообще не нужна, нам нужен атомарный счетчик ссылок на раннабл. Если в пуле лежит 2 раннаблы, объект будет жив пока не исполнится последняя. Если мы держим ссылку вне пула, то тоже всё хорошо — пул ничего не удалит, а просто выкинет свои копии указателя.
Кода меньше, код безопаснее, нет corner-case'ов которые надо описывать в документации. Одни плюсы.
Стремление Qt заработать денег понятно, просто совершенно не ясно как игры с лицензиями и запретом на установку без аккаунта Qt помогут эти деньги заработать.
Есть мнение что никак, просто неэффективный менеджмент уже выдохся придумывать где денег заработать и делает рандомные действия ну вдруг поможет.
Никто не говорит что это неправильно — это неочевидно при чтении кода. Код должен быть самодокументируемым и читающего не надо заставлять лезть в документацию, чтобы ответить на вопрос «нет ли тут утечки».
Примеры с addAction тривиальны, но есть места где приходится реально углубляться в чтение.
На самом деле, QLayout::addWidget гораздо лучше смотреть как пример. Я вот написал что он виджет репарентит, а на самом деле, он репарентит его не всегда, а только если лайаут чему-то назначен. Если у вас есть лайаут, ни к чему (ещё) не привязанный, вы сделали layout->addWidget(new Widget) и внезапно решили выйти из функциии (ну, например, настройки не считались)… Упс, утечка.
Да, это сомнительный corner-case, но его можно было бы избежать, использовав умные указатели.
Как Вы собираетесь это реализовывать через unique/shared_ptr
Легко и изящно, parent-child отлично реализуется на unique_ptr (и можно даже сделать так что старый код будет работать).
0. QObject
1. TreemodelItem
2. Нода дерева
Вообще какую пользу даст эта замена?
Эта замена даст ту пользу что будет сложнее выстрелить себе в ногу (а сейчас сделать это ооочень легко) и передача владения будет явно выражена в коде:
А все же — какую проблему планировали решить данным proposal'ом?
Абсолютно неочевидная передача владение в куче случаев. QMainWindow::setCentralWidget репарентит виджет. QAbstractItemModel::setModel не репарентит модель несмотря на абсолютно аналогичное название.
QLayout::addWidget репарентит виджет.
QMenu::addAction не репарентит экшн.
Глядя на сигнатуру\вызов метода невозможно понять, передается владение или нет, что ведет к постоянным багам (не все смотрят в документацию каждый раз).
Хорошо если метод называется takeItem, можно угадать что он отдает овнершип юзеру… Но если юзер не догададется, получим утечку.
До этого даже не дошло мб?
Почитайте рассылку, там реально аргументы уровня студента (причем не от самих кутешников, а от каких-то рандомов)
Ну как раз апстрим взял то, что в Qt было очень давно: variant, future, thread, smart-pointers и многое другое.
std::variant имеет очень отдаленное отношение к QVariant (и вообще, QVariant это скорее std::any). И пришел он в стандарт из буста а не из Qt.
Аналогично, thread — тоже из буста.
Насчет указателей не уверен, но они появились задолго до стандарта в tr1 и скорее всего пришли в Qt оттуда (просто комитет тогда был слоупоком и не принимал их).
QPointer, QAtomicPointer, QSharedPointer and QWeakPointer — не то?!
Они там ест но даже новый код пишется без их использования, например, QThread::create который возвращает голый указатель на созданный тред. Каждый раз когда я использую этот метод, я его заворачиваю в unique_ptr.
Или например тредпул который берет раннаблы с булкой «надо ли удалять раннабл» (autoDelete), что приводит к рейсу, который описан в документации (кек). А всего-то надо передавать шаред_птр на раннабл — и булка не нужна и рейс уходит.
Из всех smart pointer в самой Qt активно используется только QPointer. Что мешает пойти дальше и начать использовать остальные указатели — яхз.
Многопоточка никак не завязана на parent-child, вся иерархия «в треде» должна иметь нулевой parent (нельзя переместить в тред только часть иерархии, вы получите ворнинг).
Потом, никто не предлагает убирать иерархию, предлагают реализовать ее через unique/shared_ptr (и там уже есть счетчик слабых ссылок (а точнее вся дата QShared/QWeakPointer) в QObject для QPointer'а, например)
Автор такого высказывания активно принимал участие в обсуждении перепиливания parent-child на умные указатели. И аргументов типа «сломается много кода» там нет, аргументы там были «да посмотрите вон на модуль ХХХ (вроде что-то с Xml) он написан на смартпоинтерах и там такооой говнокод». Изменение почти неломающие (кроме ряда случаев), старый код продолжает работать и копилироваться, но мы не будем его внедрять просто потому что мы в 2к20м не уверены что смарт поинтеры это хорошо. Это дословно аргументация противников, можете почитать development mail list.
Вы уж извините, но в госструктуру я пойду в последнюю очередь=) Я лучше выучу rust или go (да даже php вспомню!) чем буду писать на уютной кутешечке на государство=)
Почему? Только если вы продолжаете навешивать noexcept выше по стеку как в вашем примере ниже.
Это валидный поинт и так делать надо далеко не всегда — если функция достаточно сложная или использует другие функции, о содержимом которых нельзя делать предположения (например, внешняя библиотека), то noexcept выше по стеку навешивать не надо. Никто и не предлагает, вообще-то.
По сути, разговор идет о значениях по умолчанию — по дефолту функцию надо делать constexpr noexcept const (ничего не забыл?) если имплементация позволяет.
Выше по стеку это не предлагается распространять, особенно если у вас там начинается бизнес-логика.
Проблема как раз в том, что из-за вышеназванной аргументации noexcept не пишут вообще нигде, даже в тех тривиальных случаях, когда имплементация метода не позволяет что-либо выбросить никогда в будущем (скажем, to_string от енума, возвращающий std::string_view от данных в RO секции).
Ну так это аргумент противоположного лагеря, лагеря «не ставить везде noexcept».
Основной посыл в том, что нетровающих методов достаточно много, может даже больше, чем тровающих. С++ вообще славится неправильными дефолтами — надо не пинать людей которые explicit у конструкторов не пишут, надо было делать их explicit по дефолту.
К примеру, если у вас геттер возращает std::string_view (или const-ref), то он, скорее всего, noexcept. Даже если вы захотите потом возвращать std::string как-то хитро посчитанный, сигнатура метода (и бинарная совместимость, если она вас заботит) уже будет сломана и наличие noexcept тут врядли будет самой большой проблемой.
Если же вы не разрабатываете библиотеку и вам не важны такие вещи, то пишите noexcept везде — потом можно снять и просто пересобрать код. Шанс того, что кто-то будет делать оптимизации наподобие тех что делает std::vector, проверяя is_nothrow_move_constructible для вашего геттера/алгоритма, исчезающе мал.
Ну типа да, может быть ситуация когда люди увидят noexcept и вместо unique_ptr где-нибудь заюзают delete (ведь так быстрее! и ислючения не тровают, вот же noexcept!) и потом, когда noexcept уберут, и в этом коде пролетит исключение, объект утечет… Ну так не надо заниматься premature optimisation и закладываться на это, пока профайлер не скажет что «вот это RAII тут тормозит».
К сожалению, x86 и друзья являются «strong ordered» архитектурами и поймать instruction reorder там сложно (вот хорошая статья, демонстрирующая, как его всё-таки словить), а значит ваш супер-мега «локфри» на волатайлах будет успешно «работать», возможно в продакшне и не один год.
Это, кстати, распространённый миф, кэши процессора когерентны и если вы в одну кэш линию что-то записали, то она «разъедется» на все ядра.
Другое дело что никто не гарантирует что запись будет осуществлена в том месте где вы написали (процессор/компилятор вольны переставлять инструкции) или вообще осуществлена — никто же не сказал что это атомик, можно вообще запись в кэш/память выкинуть или на регистрах оставить.
Это UB
Я видел достаточное количество проектов где внедрили pch, и подсветка в половине хедеров была сломана потому что «stdafx.h» включается до всех остальных хедеров.
На машинах разработчиков можно и PCH настроить для пущей скорости.
setAutoDelete(false) нужен для того, чтобы переиспользовать раннаблы несколько раз. Например, мы хотим запускать раннабл дважды:
Теперь тот же юзкейз на shared_ptr:
Фишка в том, что мы получили тот же юзкейз и нам эта булка вообще не нужна, нам нужен атомарный счетчик ссылок на раннабл. Если в пуле лежит 2 раннаблы, объект будет жив пока не исполнится последняя. Если мы держим ссылку вне пула, то тоже всё хорошо — пул ничего не удалит, а просто выкинет свои копии указателя.
Кода меньше, код безопаснее, нет corner-case'ов которые надо описывать в документации. Одни плюсы.
Есть мнение что никак, просто неэффективный менеджмент уже выдохся придумывать где денег заработать и делает рандомные действия ну вдруг поможет.
Никто не говорит что это неправильно — это неочевидно при чтении кода. Код должен быть самодокументируемым и читающего не надо заставлять лезть в документацию, чтобы ответить на вопрос «нет ли тут утечки».
Примеры с addAction тривиальны, но есть места где приходится реально углубляться в чтение.
На самом деле, QLayout::addWidget гораздо лучше смотреть как пример. Я вот написал что он виджет репарентит, а на самом деле, он репарентит его не всегда, а только если лайаут чему-то назначен. Если у вас есть лайаут, ни к чему (ещё) не привязанный, вы сделали layout->addWidget(new Widget) и внезапно решили выйти из функциии (ну, например, настройки не считались)… Упс, утечка.
Да, это сомнительный corner-case, но его можно было бы избежать, использовав умные указатели.
Легко и изящно, parent-child отлично реализуется на unique_ptr (и можно даже сделать так что старый код будет работать).
0. QObject
1. TreemodelItem
2. Нода дерева
Эта замена даст ту пользу что будет сложнее выстрелить себе в ногу (а сейчас сделать это ооочень легко) и передача владения будет явно выражена в коде:
Абсолютно неочевидная передача владение в куче случаев. QMainWindow::setCentralWidget репарентит виджет. QAbstractItemModel::setModel не репарентит модель несмотря на абсолютно аналогичное название.
QLayout::addWidget репарентит виджет.
QMenu::addAction не репарентит экшн.
Глядя на сигнатуру\вызов метода невозможно понять, передается владение или нет, что ведет к постоянным багам (не все смотрят в документацию каждый раз).
Хорошо если метод называется takeItem, можно угадать что он отдает овнершип юзеру… Но если юзер не догададется, получим утечку.
Почитайте рассылку, там реально аргументы уровня студента (причем не от самих кутешников, а от каких-то рандомов)
std::variant имеет очень отдаленное отношение к QVariant (и вообще, QVariant это скорее std::any). И пришел он в стандарт из буста а не из Qt.
Аналогично, thread — тоже из буста.
Насчет указателей не уверен, но они появились задолго до стандарта в tr1 и скорее всего пришли в Qt оттуда (просто комитет тогда был слоупоком и не принимал их).
Они там ест но даже новый код пишется без их использования, например, QThread::create который возвращает голый указатель на созданный тред. Каждый раз когда я использую этот метод, я его заворачиваю в unique_ptr.
Или например тредпул который берет раннаблы с булкой «надо ли удалять раннабл» (autoDelete), что приводит к рейсу, который описан в документации (кек). А всего-то надо передавать шаред_птр на раннабл — и булка не нужна и рейс уходит.
Из всех smart pointer в самой Qt активно используется только QPointer. Что мешает пойти дальше и начать использовать остальные указатели — яхз.
Потом, никто не предлагает убирать иерархию, предлагают реализовать ее через unique/shared_ptr (и там уже есть счетчик слабых ссылок (а точнее вся дата QShared/QWeakPointer) в QObject для QPointer'а, например)