Задать вопрос
Ответы пользователя по тегу C++
  • Корректно ли вызывать метод у временного объекта?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    DeepSeek галлюционирует. Временные объекты живут до конца выражения. unique_ptr начнет уничтожение после выполнения всего выражения.

    Вот ссылки на стандарт:

    make_unique<> является prvalue: https://en.cppreference.com/w/cpp/language/value_c...
    prvalue: a function call or an overloaded operator expression, whose return type is non-reference


    В момент вызова происходит материализация временного объекта: https://en.cppreference.com/w/cpp/language/lifetime

    Temporary objects are created ... in the following situations:
    when performing member access on a class prvalue.


    Там же написано:
    All temporary objects are destroyed as the last step in evaluating the full-expression


    Т.е. возвращенный make_unique объект будет уничтожен только в конце строки.
    Ответ написан
    Комментировать
  • Когда передавать копию callable, а когда через rvalue reference?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В дизайне интерфейса можно передавать и копию и rvalue refernce. Что выбрать - решать вам, как создателю интерфейса. Если у вас предполагаются использование функций/лямбд без тяжелого состояния (связанные аргументы по значению), то копирование дешевое и не надо его вводить. rvalue reference всегда дешево, но у вас нельзя будет переиспользовать предикат между разными вызовами.

    В функциях из algorithm используется передача по значению, что бы не ограничивать пользователя. Вы можете один раз написать нужный вам предикат и передавать его во все вызовы. Плюс исторически так сложилось. Когда оно все созадавалось, никаких rvalue reference и не было. Потом их добавлять и не стали, потому что в самом частом случае, если лямбду прямо в аргументах задавать, то она практически бесплатно передастся.

    Там не используется передача по ссылке (lvalue reference), потому что это не так универсально в шаблоне. Если у вас какой-то const предикат, вы его в reference не передадите. А делать const & в интерфейсе нельзя, вдруг у вас предикат не константный?

    И, если вы все-таки хотите передать ссылку, то можно передать std::ref(pred), тогда в шаблоне выведется передача по ссылке.
    Ответ написан
    Комментировать
  • Правильно понимаю из статьи про умные указатели?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Похоже на ошибку оформления. Это должен быть вызов функции. В тексте выше даже написано про вызов.

    Тут автор считает, что сначала выполнится new A(), потом new B(), потом конструктор unique_ptr. Если исключение бросит B(), то действительно будет утечка памяти. Объект A, полученный через new умрет еще до оборачивания в unique_ptr. Такой сырой указатель автоматически не удалится.

    Такая последовательность невозможна c С++17:
    In a function call, value computations and side effects of the initialization of every parameter are indeterminately sequenced with respect to value computations and side effects of any other parameter.


    Evaluations of A and B are indeterminately sequenced : they may be performed in any order but may not overlap: either A will be complete before B, or B will be complete before A. The order may be the opposite the next time the same expression is evaluated.


    Но до C++17, действительно, компилятор может перемешать вычисления аргументов как угодно.
    Ответ написан
    Комментировать
  • Код при самостоятельном тестировании работает корректно, а при проверке тестировщиком программа выдает ошибку. В чем может быть проблема?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    У вас неинициализированные переменные mini и maxi. И они используюся такими в сравнениях. Это Undefined Behavior и на разных компьютерах может вести себя по разному. И если вам не повезет и, например, mini окажется каким-то очень маленьким числом, то у вас выдаст не правильный ответ.
    Ответ написан
  • Как реализовать алгоритм на С++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Используйте std::vector. Их можно присваивать как переменные. При этом там под капотом не будет никакого копирования, а просто изменение указателей в данном случае (так как правая часть - временное значение).
    std::vector<int> a, b;
    a = a0;
    b = F(a);
    for (int i = 1; i <= n; ++i) {
      a = G(b);
      b = F(a);
    }


    Если вас напрягает, что функции G и F выделяют память внутри, то можно сделать, чтобы они получали vector, в который надо вернуть значения:
    void F(const vector<int> &a, vector<int>& res);
    void G(const vector<int> &b, vector<int>& res);
    
    std::vector<int> a, b;
    a = a0;
    b = F(a);
    for (int i = 1; i <= n; ++i) {
      G(b, a);
      F(a, b);
    }
    Ответ написан
    Комментировать
  • Почему без std::remove_reference_t не работает?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вообще, не вижу противоречия. У вас же там tfunc&&, его вы пытаетесь к void* привести и к нему применяете remove reference.

    Попробуйте в ваш static assert добавить std::move и он обвалится.
    Ответ написан
    Комментировать
  • Могут ли возникнуть проблемы при одновременном чтении и записи в разных потоках переменной?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ничего страшного не произойдет, да. Просто вы никак не проконтролируете, когда запись из первого потока будет видна во втором потоке. Например, вы тут можете увидеть скачек сразу на 2, или несколько раз подряд одно и то же значение, хоть оно по идее должно постоянно и монотонно расти.
    Ответ написан
    Комментировать
  • Что лучше: static методы или функции?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Посмотрите в этот allStatic: https://github.com/openjdk/jdk/blob/496641955041c5...

    Там написано, почему используются статические методы: Почему-то авторы какого-то проекта HotSpot решили, что плодить namespac'ы плохо. Так что это вызвано соглашениями по стилю в конкретном проекте. Их право.

    Вообще говоря, польза от статических методов в том, что у них автоматически есть доступ к приватным членам класса и не надо каждую функцию помечать friend. Если у класса все методы статические и нет никаких данных, то использовать статические функции нет смысла.

    Еще логично сделать функцию членом класса, если она именно с классом работает. Например, функции фабрики.

    Кроме этого я не вижу особо причин использовать статические методы вместо функций.
    Ответ написан
    2 комментария
  • Почему шаблон выдает ошибку при включении заголовка в .cpp файл?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Не совсем уверен, потому что кода не хватает в вопросе. Но симптом звучит как "если шаблон определяется не в том же файле, где он объявляется, то ничего не работает". Это частая ошибка на С++ с шаблонами.

    Когда компилятор видит, что шаблон используется, он генерирует код с конкретной спецификацией шаблона. Но делает он это только в текущем юните трансляции. Так что если у вас шаблон определен в другом cpp файле, то компилятор эти определения не видит и не может сгенерировать их спецификацию, когда обрабатывает cpp с использованием шаблона. А когда дело доходит до cpp с шаблоном, то компилятор не видит, где и как шаблон используется и тоже не генерирует никакой спецификации.

    Поэтому надо или шаблон объявлять и опеределять в хедере, чтобы оперделения были везде, или в cpp с определениями указать спецификацию для всех используемых варинтов шаблона. Вроде
    using my_template<int>;
    Ответ написан
    6 комментариев
  • Какая функция (или набор разных ф-ий) изменения "мощности" цвета света при распространении луча?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Это называется яркость света. Цвет лазера особо не меняется. Наверно, можно считать, что яркость уменьшается очень медленно линейно из-за рассеивания воздухом. Для не лазерных источников света яркость убывает квадратично от расстояния.
    Ответ написан
  • How to fix packing error in ue5?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Try doing what the error messages are telling you:

    Detected compiler newer than Visual Studio 2022, please update min version checking in WindowsPlatformCompilerSetup.h


    Or install the Visual Studio 2022 instead of what you have.
    Ответ написан
  • Почему в c++ еще нету Null-Conditional Operator?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Это не достаточно частая операция в С++, чтобы срочно надо было вводить новый оператор в синтаксис. Комитет занят более интересными вещами на годы вперед.
    Ответ написан
    3 комментария
  • На чём создать прогу для обработки больших данных?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    100gb - это не так и много. Даже не надо кластера, чтобы это обрабатывать.

    Быстрее, конечно, работать будет на C++/rust каком-нибудь, но обработка csv может быть легче реализованна на питоне.
    Ответ написан
  • Есть ли разница между *p++ и p++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Разница как бы есть. В одном случае у вас *(p++), а в другом просто p++. И то и другое сдвинет указатель p на одну ячейку вправо, т.е. код имеет ровно тот же результат. Но в случае с * вы еще и адрес p, который там был до увеличения, разименуете, т.е. получите доступ. Но просто такое выражение, где вы его разименовываете и ничего с ним не делаете не имеет смысла. Его можно использовать, если вы со значением что-то делаете, например:
    void Copy(char *src, char *dst) {
        while (*src) {
          *dst++ = *src++;
        }
        *dst = '\0';
      }


    Тут вы значение по адресу src берете и записываете в адрес dst. Но из-за ++ оба указатлея сдвинутся. Получается копирование сишной строки.

    Но делать просто *p++; смысла никакого нет. Это примерно то же что и:
    int i;
    i;

    Вот выражение `i;` - оно как бы получает доступ к i, но со значением ничего не делает. Это странный и бесполезный код.

    Так что * у вас там явно лишняя.
    Ответ написан
    Комментировать
  • Как скомпилировать экзешник из гитхаба?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В ошибке же написано "Unexpected compiler version, expected CUDA 10.1 Update 2 or newer."

    Перевод на русский - поставьте CUDA версии 10.1 update 2 или новее.
    Ответ написан
    3 комментария
  • Как "выпрямить" кольцевой буфер c ограниченной доп.памятью?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ваша задача - сделать циклический сдвиг массива на k позиций влево на месте, с O(1) дополнительной памяти.
    Вот код:
    void ShiftLeft(std::vector<int> &a, int k) {
      int n = a.size();
      int cycles = std::gcd(n, k);
      for (int start = 0; start < cycles; ++start) {
        int tmp = a[start];
        int i = start;
        while (true) {
          int next = i + k;
          if (next >= n) next -= n;
          if (next == start) break;
          a[i] = a[next];
          i = next;
        }
        a[i] = tmp;
      }
    }


    Работает это так: на место элемента вставет элемент на k позиций правее. Возьмем первый элемент, запомним его в tmp, поставим на его место тот, кто там должен быть. Освободилось место, поставим на него опять того, кто там должен быть и так далее. Рано или поздно мы вернемся к первому элементу. Но там уже стоит второй. Тут можно выйти из цикла и поставить на нужное место тот, кого мы сохранили в tmp. Но мы могли обойти не весь массив, как, например в случае n=6, k=2. Начав с 0 мы тут подвинем элементы 0,2,4, но не 1,3,5. Всего будет циклов gcd(n, k), и они все идут со сдвигом 1. Поэтому можно начинать с каждого из нескольких первых элементов.

    Додуматься до этого можно так: сдвиг на 1 позицию понятно как циклом while сделать-то и одной переменной tmp. А на несколько? Надо только заметить что элементы разбиваются на циклы.
    Ответ написан
    6 комментариев
  • Как сделать безопасную многопоточную очередь?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Самое простое, конечно, это иметь только один мьютекс на всю очередь и лочить его при любой работе с ней. Ваша концепция имеет много проблем. Например, а что если у вас только один элемент? Тогда он и начало и конец, но в зависимости от того, что вы там вызываете, вы будете блокировать только один из двух мьютексов.

    Если вы гонитесь за эффективностью и параллельностью, то вам лучше написать lock-free структуру данных. Гуглите эти ключевые слова. Но это очень сложно.

    Еще, как вариант, можно физически разбить очередь на 2 куска. Один для записи, другой для чтения. Т.е. у вас 2 очереди, каждая со своим локом. При доступе к голове вы блокируете мьютекс на чтение и читаете из очереди на чтение. При доступе к хвосту - на запись. Записывать тривиально - просто лочте мьютекс на запись и добавляйте элемент в очередь на запись. При чтении лочте очередь на чтение и читайте из нее. Но, если вы выяснили, что очередь на чтение оказалась пуста, то надо залочить еще и очередь на запись и поменять их местами (т.е. перекинуть всю очередь записи в чтение). И потом вернуть голову этой очереди.

    Такой подход позволит более-менее параллелить чтение и запись, особенно, если очередь часто не пуста.

    Лочить только один эелемент с каждого конца сложно - там очень много случаев. Лоча 2 половины отдельно вы можете не думать о другой большую часть времени.
    Ответ написан
    5 комментариев
  • Разрезание многоугольника горизонтальными линиями. Алгоритм?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Основаня идея простая: ориентируйте многоугольник, допустим, против часовой стрелки. Потом для каждой точки пересечения надо знать, входит ли в ней многоугольник внутрь, или выходит наружу из нужной области. Потом обходите многоугольник начиная с какой-то точки пересечения, пока отрезки снаружи - пропускаете их, пока внутри - дописываете в ответ. Надо еще помнить, где у вас последний отрезок в ответе заканчивается, так что иногда новый отрезок будет с ним не связан - это надо будет взять отрезки из прямоугольника в ответ. Но есть крайние случаи: пересечение по прямой, многоугольник выходит за границу в одной стороне, а входит в другой.

    Самая концептуально простая реализация будет, если сделать функцию для отсечения группы многоугольников полуплоскостью. Прямоугольник ваш - это 4 полуплоскости: 2 вертикальные и 2 горизонтальные. Пересечение с полуплоскостью проще - там тупо прямая же одна. Вот пресеките многоугольник с первой, полученные ответ со второй, потом что осталось с третьей и потом с четвертой полуплоскостью.

    Удобно хранить множество точек и для каждой - указатель на сделующую в обходе многоугольника (ориентированного так, что слева от отрезка внутренность многоугольника). В таком виде входные данные, в таком виде будет и ответ.

    Точки будем помечать, как обработанные.

    Обойдите все точки циклом, для всех необработанных еще, которые лежат внутри полуплоскости, начинайте с них строить ломанную-ответ. Идите к следующей точке, пока отрезок не пересечет полуплоскость (это значит конец строго внутри удаляемой области), помечая точки обработанными. До этого момента добавляйте точки в ответ сохраняя указатель на следующую точку. Для текущей следующей сделайте новую точку - точку пересечения. Еще запомните где-то эту точку пересечения, это потом понадобится. Продолжайте обход, пока отрезок не выйдет из полуплоскости (конец строго снаружи удаляемой области). Тут создайте точку пересечения, запишите ее в ответ, и для нее следующей сделайте конец отрезка снаружи. Продолжайте обход как в начале. Если встретили уже обработанную точку, вы закончили строить ломанную ответ.

    Потом надо будет все точки пересечения соединить в порядке вдоль прямой парами (смотрите так, чтобы удаляемая область была справа от направления движения). Их будет четное количество.

    Вот у вас все оставшиеся точки образуют многоугольники, если идити по указателям на следующую. Правада, они в массиве могут идти вперемешку, а не подряд. В конце вам может понадобится их сгруппировать, но это делается похожим обходом: помечайте точки обработанными, берите любую не обработанную и идите от нее по указателям, пока не вернетесь в нее же, помечая все точки как обработанные и добавляя в ответкак часть одного многоугольника. Это можно сделать после того, как пересечете со всеми 4 полуплоскостями.

    Удобно прямую хранить в виде Ax+By+C=0. И знак подобрать так, что точки внутри хорошей области, если их подставить в это уравнение дают положительные значения (а точки снаружи - будут отрицательные). Соответственно, проверка, что с точки можно начать обход - просто посмотреть на знак. Проверка, что есть пересечение - конец отрезка дает отрицательный знак (а потом - положительный). Точки удобно хранить в виде массива координат и массива номеров следующей точки в обходе. Никаких сишных указателей - номера следующей точки в массиве. Сортировать точки пересечения надо будет по значению Ay-Bx.

    Если вам надо различать изначальные точки многоугольника и добавленные, то можете это свойство у точек копировать из входных данных в ответ, когда точка добавляется в ответ из многоугольника, а не создается новая точка пересечения.

    Это все удовольстивие будет работать за O(n log n) - потому что надо потом точки пересечения на прямой отсортировать.
    Ответ написан
  • Объясните, пожалуйста, смысл такого фрагмента кода класса _Iosb файла xiosbase?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Правильно ли понимаю, что после присвоения _Fmtflags skipws = (_Fmtflags)_IOSskipws; _IOSskipws становится челном перечисления _Fmtflags ?


    Нет, сначала препроцессор заменит _IOSskipws на 0x0001, потом skipws инициализируется 0x0001.

    Зачем так сделано? Может, кто-то очень жестко следует правилу не использовать магические числа в коде. Все константы должны быть как-то определены заранее. Видимо, в стиле, принятом тут, константы заводят через define.

    Или IOSskipws используется в инициализации каких-то еще констант в этом или другом классе и тогда имеет смысл заводить его отдельно.

    Оказывают ли поля _Fmtmask = 0xffff, _Fmtzero = 0 перечисления _Fmtflags, какое либо влияние на поля _IOSskipws


    _IOSskipws - это вообще не поле, это символ, заменяемый препроцессором на 0x0001. Он с точки зрения С++ вообще не существует.
    Ответ написан
    Комментировать
  • Не работает кнопка сервис. как исправить?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    break; в коде все портит. Выполняется первая проверка, не срабатывает, наверно, потому что currentState не тот. А может, потому что у вас там еще break между уловными проверками расставлены. До проверки на BUTTON_ID_CLOSE_SERVICE код никогда не доходит.

    Break должен быть один раз в конце case блока, чтобы управление не перешло на следующий case. Switch же просто переносит управление на соответствующий case и все. Он не отключает как-то куски кода в других альтернативах.
    Ответ написан