• Как правильно перегрузить шаблонный оператор (метод, функцию), чтобы наследники попадали в нужный?

    @Mercury13
    Программист на «си с крестами» и не только
    Итак, перед нами конфликт первой и второй функции, и надо первую как-то ограничить.

    Вариант 1. Концепция Си++20.
    template <class T>
    concept Printable = requires(T x) {
            std::cout << x;
    };
    
    struct Class {
        template<Printable Text>
        Class& operator<<(const Text& text) {
            cout << text << endl;
            return *this;
        }


    Вариант 2. Обратная концепция.
    template<uint8_t i>
    struct Id {
        constexpr static uint8_t id = i;
        using SpecialPrint = void;
        // какие-то элементы класса с методами
    };
    . . . . .
    template <class T>
    concept SpecialPrintable = requires {
        typename T::SpecialPrint;
    };
    
    struct Class {
        template<class Text>
        Class& operator<<(const Text& text) {
            cout << text << endl;
            return *this;
        }
        
        template <SpecialPrintable Special>
        Class& operator<<(const Special& text) {
            specialPrint(text);        
            return *this;
        }
        
        template<uint8_t i>
        void specialPrint(const Id<i>& text) {
            cout << (int)i << endl;
        }
    };


    А на 17 без концепций…
    template<uint8_t i>
    struct Id {
        constexpr static uint8_t id = i;
        using SpecialPrint = void;
        // какие-то элементы класса с методами
    };
    . . . . .
    
    template<class T, class Dummy = void>
    struct IsSpecPrintable { static constexpr bool value = false; };
    
    template<class T>
    struct IsSpecPrintable<T, typename T::SpecialPrint> { static constexpr bool value = true; };
    
    struct Class {
        template <class T>
        Class& operator<<(const T& text)
        {
            if constexpr (IsSpecPrintable<T>::value) {
                specialPrint(text);
            } else {
                normalPrint(text);
            }
            return *this;
        }
    
        template<class Text>
        void normalPrint(const Text& text) {
            cout << text << endl;
        }
    
        template<uint8_t i>
        void specialPrint(const Id<i>& text) {
            cout << (int)i << endl;
        }
    };
    Ответ написан
    1 комментарий
  • Безопасен ли класс для многопоточности?

    @res2001
    Developer, ex-admin
    Не безопасен.
    Вы защитили мьютексом только запись в очередь (push), но не чтение из нее (pop). Так что очередь сломается рано или поздно.
    Вообще тут лучше бы подошла какая-то специализированная структура со встроенной поддержкой многопоточности, а не стандартные контейнеры.
    Можно использовать кольцевой буфер или очередь майкла-скотта. В booste, на сколько помню, есть и то и другое.
    Но для начала сгодится и такой вариант.

    Кроме того для вывода на экран в Линукс можно учитывать тот факт, что ОС обеспечивает атомарную запись в консоль для буфера менее PIPE_BUF байт. https://linux.die.net/man/7/pipe
    Думаю в винде то же есть похожая гарантия, но это не точно.

    Кроме того, операция << для логгирования не очень подходит, т.к. вынуждает использовать конструкции типа:
    oss << "Error: " << strerror(err) << "(" << err << ")";

    для вывода 1 строки. А это уже не одна запись в очередь, а несколько. Так что защита мьютексом тут не будет гарантией вывода целостной строки.
    Для логгера больше подойдет вариант типа std::printf, когда в один вызов передается вся информация для генерации целой строки.
    Ответ написан
    4 комментария
  • Почему консоль всегда выводит, что я програл?

    @rPman
    c = rand() % 2;у тебя тут ошибка, c у тебя равно символу с кодом либо 0 либо 1,
    if (u == c)
    а сравниваешь с символом '0' либо '1', у них код 48 и 49 соответственно

    поэтому добавляй к c еще код символа '0' (он в ascii кодировке перед '1')
    c = rand() % 2+'0';
    p.s. из-за исторических наслоений, символ не считается несовместимым типом с числом, иначе бы ты увидел эту ошибку в сообщениях компилятора
    Ответ написан
    Комментировать
  • Насколько хорошая практика использовать обертки над операторами?

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

    Но вы этот оператор передаете всего в нескольких местах на всю кодовую базу и все. Писать так функции, которые вы используете кучу раз - очень плохая идея.

    И вообще, довольно редко есть смысл так заморачиваться. Код становиться хуже поддерживаемый. Экономите вы совсем чуть чуть повторяемого кода. А потом придется всякие заморочки делать, вроде, в делении надо проверять на 0, а в умножении проверять на переполнение и т.д. Этот общий код становиться уже чуть ли не длиннее если все по отдельности делать. И уж точно хуже читаемый.
    Ответ написан
    Комментировать
  • Как переделать код согласно современным стандартам?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы с неинициализированной памятью работаете почти правильно, но…

    Первое. Надо указать выравнивание класса.
    MyClass* arr = (MyClass*) operator new (sizeof(MyClass) * 10,
                                            std::align_val_t(alignof(MyClass)));
    . . .
    operator delete(arr, std::align_val_t(alignof(MyClass)));


    Второе. А нельзя инкапсулировать подобную работу с памятью в класс, чтобы конструктор выделял память и деструктор отдавал? Я сделал вот такое.
    #include <iostream>
    
    class MyClass {
    private:
        const int a;
    public:
        MyClass() = delete;
        MyClass(MyClass&&) = delete;
        MyClass(const MyClass&) = delete;
        MyClass(int a) : a(a) {};
        ~MyClass() = default;
        MyClass& operator=(MyClass&&) = delete;
        MyClass& operator=(const MyClass&) = delete;
        operator int() const { return a; }
    };
    
    template <class T>
    class DynArray {
    public:
        static constexpr std::align_val_t ALIGNMENT { alignof(T) };
        DynArray (size_t n)
            : maxSize(n),
              d((T*) operator new (sizeof(T) * n, ALIGNMENT )) {}
        ~DynArray() {
            std::destroy_n(d, currSize);
            operator delete(d, ALIGNMENT);
        }
        template <class... Args>
            T& add(Args&&... args);
        T& operator [] (size_t i) { return d[i]; }
        const T& operator [] (size_t i) const { return d[i]; }
    private:
        size_t currSize = 0, maxSize = 0;
        T* d;
    };
    
    template <class T> template <class... Args>
    T& DynArray<T>::add(Args&&... args)
    {
        if (currSize >= maxSize)
            throw std::logic_error("[DynArray::add] Overflow");
        auto& where = d[currSize++];
        new (&where) MyClass(std::forward<Args>(args)...);
        return where;
    }
    
    int main() {
        DynArray<MyClass> da(10);
    
        for (int i = 0; i < 10; ++i) {
            da.add(i);
        }
    
        // Что-то сделали с массивом
        for (int i = 0; i < 10; ++i) {
            std::cout << da[i] << '\n';
        }
    
        return 0;
    }


    Ну и третье — в коде есть, но забыл указать — разбирайте API неинициализированной памяти Си++17.

    ЧЕТВЁРТОЕ. Есть один класс, который не перемещает. Поскольку начиная с Си++17 весь STL внутри работает с неинициализированной памятью, получается довольно мило — но хранится ли оно непрерывно, мы никак не узнаем.
    std::deque<MyClass> dq;
        for (int i = 0; i < 10; ++i) {
            dq.emplace_back(i + 42);
        }
        // Что-то сделали с массивом
        for (int i = 0; i < 10; ++i) {
            std::cout << dq[i] << '\n';
        }
    Ответ написан
    2 комментария
  • Как создать многомерный массив в одной области памяти?

    AshBlade
    @AshBlade
    Просто хочу быть счастливым
    Одномерный массив размером X * Y - единственное решение, если нужен непрерывный участок памяти.
    Для получения первого индекса - index / X, для второго - index % X.
    Но надо позаботиться - чтобы места было достаточно, иначе однажды получишь OOM либо когда место закончится, либо при сильной фрагментации памяти.

    Вариант с "зубчатым" массивом, тоже норм - отложенное выделение можно реализовать. Но по скорости будет проигрывать из-за локальности данных. Хотя, если нужен непрерывный участок - уже не подходит
    Ответ написан
    2 комментария
  • Как создать многомерный массив в одной области памяти?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Есть еще 2 варианта:

    1) Используйте std::vector<std::array>.

    2) Что-то среднее между двумя вариантами у вас: как и везде, у вас есть одномерный массив для данных. А operator[] на классе возвращает int* на первый элемент в строке. То же, что у вас, только не надо никакого вспомогательного класса. Таким образом двойная индексация будет работать как надо. Но, как и во втором упомянутом вами примере, тут не будет проверки выхода за границы массива.
    Ответ написан
    Комментировать
  • Как реализовать операторы в классе математического вектора?

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

    Вектора можно перемножать. Скалярное или вектороное произведения. Чаще скалярное ставят на operator*, а векторное на какое-нибудь operator^

    Операторы сравнения обычно делают лексикографически, покомпонентно. Сравнили первое число - у какого вектора больше - тот и больше. Если числа равны, сравниваем вторые компоненты. И т.д. Но это делают редко, потому что смысла вектора сортировать особо нет. Лексикографический порядок пользы особо не приносит.
    Ответ написан
    9 комментариев
  • Что быстрее индексы или указатели?

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

    Практический совет - лучше писать через индексы, ибо так понятнее и больше шансов что компилятор там все наоптимизирует (например, он сможет векторизовать работу через какие-нибудь SSE инструкции процессора).

    Совет по бенчмарку - если памяти не хватает, стоит по одному достаточно большому массиву пройтись 10000 раз. А лучше использовать готовые фреймворки для измерения скорости, вроде того де gbenchmark.

    Еще, иногда полезно посмотреть на ассемблерный выхлоп. Вот, например, что происходит при -O3 опции компилятора. Он генерирует вообще идентичный код для обеих функций (развернув циклы)! И даже при -O2 оно одинаковый код выдает.

    Без оптимизаций код разный, но там все не так как вы думаете. Вместо инструкции mov eax, dword ptr [rax + 4*rcx] в варианте с индексами используется инструкция mov eax, dword ptr [rax] для указателей. Это самое "складывание с указателем массива" вообще не отдельная операция - а вариант адрессации в инструкции mov. Они могут вообще одинаковое количество тактов занимать, это надо мануал по конкретной архитектуре процессоров читать.
    Ответ написан
    Комментировать
  • Не удается сопоставить определение функции существующему объявлению. Как можно исправить?

    @dima20155
    you don't choose c++. It chooses you
    Для этих целей вам необходим ещё один шаблонный параметр.
    Можно сделать вот так:
    template <const size_t c, const size_t b = c*c>
    struct Sudoku_impl{
        size_t s;
        Sudoku_impl() {std::cout << b;}
    };
    
    int main(){
        constexpr size_t x = 5;
        Sudoku_impl<x> a;
        return 0;
    }

    выглядит сомнительно из-за возможности переопределить второй шаблонный параметр, поэтому наружу такой судоку-класс отдавать не стоит. Чтобы решить эту проблему можно добавить псевдоним, ограничивающий количество шаблонных параметров до одного
    например так:
    template <const size_t c>
    using Sudoku = Sudoku_impl<c, c*c>;


    или просто напишите шаблон для конкретной функции
    template <const size_t cell>
    class Sudoku {
    private:
        class Slot;
        arr2<Slot, size> board;
    public:
        template <size_t size = cell*cell>
        arr<Slot, size>& operator[](size_t index);
    }
    Ответ написан
    Комментировать
  • Как использовать класс объявленный в другом файле?

    xzripper
    @xzripper
    0xC0000005
    Измените extern Logger logg на Logger logg;

    Еще немного улучшил код:
    #include <iostream>
    #include <fstream>
    
    class Logger {
    public:
        Logger() {
            openFile("Logger.txt");
        }
    
        ~Logger() {
            closeFile();
        }
    
        template<class T>
        Logger& operator<<(const T& value) {
            file << value;
    
            std::cout << value;
    
            return *this;
        }
    
        Logger& operator<<(std::ostream& (*manipulator) (std::ostream&)) {
            file << manipulator;
        
            std::cout << manipulator;
    
            return *this;
        }
    
        void openFile(const char *path) {
            file.open(path);
    
            if(!file.is_open()) {
                throw std::runtime_error("Failed to open 'Logger.txt'");
            }
        }
    
        void closeFile() {
            file.close();
        }
    
    private:
        std::ofstream file;
    };
    
    Logger logger;


    logger << 6 << " is six" << std::endl;

    64e1e14d1a6e4270143548.png
    Ответ написан
    Комментировать
  • Как использовать класс объявленный в другом файле?

    maaGames
    @maaGames
    Погроммирую программы
    Сразу скажу, что это плохой код и делать так не надо.
    Однако, отвечая на вопрос: весь этот код написан в .h файле. Нужно сделать .cpp файл, в котором будет сздана переменная logg. Сейчас объявлено, что переменная есть, но самого объекта нет.
    Ответ написан
    4 комментария
  • Нужно ли делать защиту при делении на ноль?

    mayton2019
    @mayton2019
    Bigdata Engineer
    Вообще ты сам себе ответил на вопрос
    в разных отраслях для личного пользования.


    Главный пользователь и заказчик - это ты. И когда ты разрабатываешь свою библиотеку векторной алгебры - то пишешь модульные тесты. И тесты, как-бы документируют твою библиотеку и закрывают все вопросы
    с нулями и бесконечностями.

    Какие вообще у тебя варианты по отработке неопределенностей? Я вижу такие.
    1) Возвращать бесконечность +Inf. Это нормально для floating-point. Но влияние результата на стек
    вызовов дальше надо учитывать. Эта бесконечность пойдет в другие формулы порождая новые бесконечности и т.п.

    2) Бросать исключение. Это не в духе С++ и не всегда удобно для выскокой производительности. Но языки высокого уровня этим часто пользуются. Здесь мы предполагаем что такой результат - крайне нежелателен и работа стека расчета векторов будет аварийно прервана.

    3) Возвращать специальный контейнер с результататом (Optional или Either) или пустой контейнер. Это в духе функционального кодинга. Но весь твой стек должен тоже быть адаптированным к таким Optional параметрам результатов.

    И есть еще вариант - просто найти такой базис вычислений в котором нет такой проблемы. Пускай допустим векторы так и остаются основным типом данных но расчеты ты будешь делать в каком-нибудь другом типе где эта операция на уровне математики - безопасна и всегда определена. Грубо говоря как в углах Эйлера. Чтоб не писать всякие проверки условий (if) - можно перейти к кватерниону и там вроде как вращение легче идет.
    Ответ написан
    Комментировать
  • Нужно ли делать защиту при делении на ноль?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Для облегчения отладки в будущем, стоит вставить assert какой-нибудь. Чтобы программа падала с понятной ошибкой, при попытке поделить на 0.
    Ответ написан
    Комментировать
  • Как оформить код?

    @dima20155
    you don't choose c++. It chooses you
    самое простое - используйте typedef или using
    А также можно написать свой класс-обертку для удобства.
    Ответ написан
    3 комментария
  • Как определить есть ли противоречия в цепочке логических выражений?

    wataru
    @wataru Куратор тега Математика
    Разработчик на С++, экс-олимпиадник.
    Алгоритм называется "обход в глубину на графе". Работает за линию, все очень быстро. Правда, его придется применить несколько раз.

    Все неравенства "==" замените на пару "<=" и ">=".
    Добавьте неравенства 1 < 2, 3 < 4 и т.д. для каждой пары соседних на числовой прямой чисел во входных данных

    Постройте граф: Каждой переменной и уникальному числу во входных данных сопоставьте одну вершину. Проведите для каждого неравнества ребро от меньшей вершины к большей, раскрашенное в 2 цвета: черный, если неравнество нестрогое (<=), белый - иначе.

    Теперь, если в этом графе нет циклов, содержащих белые ребра (строгие неравенства) - то противоречий нет: Все циклы целиком из черных ребер означают, что все вершины имеют одинаковое значение. Можно эти вершины все объединить в одну новую. Раз белые ребра (<) циклов не образуют, то получившийся граф будет ациклическим и можно назначить всем вершинам какие-то числовые значения, удовлетворяющие условиям. Проблема может еще быть, что нет целых решений вроде 1== a < b < c == 2, но это можно потом проверить в топологической сортировке жадно назначая всем вершинам числа. Или противоречия вида 2==3. Тоже решается после получения компонент связности.

    Итак, алгоритм: найдите в этом графе компоненты сильной связности. Потом проверьте все ребра. Если белое ребро (строгое неравнество) имеет оба конца в одной и той же компоненте - вы нашли противоречие.

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

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

    В конце вы получите для каждой компоненты ее численное значение без каких-либо противоречий.
    Ответ написан
    4 комментария
  • Калькулятор C++ как убрать 1.33333e+06 подобные результаты вычисления?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Выводить в фиксированном виде:
    std::cout << std::fixed;  // Меняем формат вывода вещественных чисел 
    std::cout.precision(10);  // Сколько вы там хотите знаков после запятой выводить.
    double e = 1.3333e6;
    std::cout << e;  // 1333300.00000000000;
    Ответ написан
    1 комментарий
  • List убирает string значения из класса, что делать?

    @dima20155
    you don't choose c++. It chooses you
    Во-первых, зачем вы приводите целый исходник сюда. Люди куда быстрее поймут суть, если вы сократите иерархию классов до демонстративного минимума.

    Во-вторых, во всех ваших case'ах
    Создается временный объект, затем вы зачем-то создаете ещё и временную переменную-указатель и сохраняете его адрес (зачем?). А после запихиваете все это в лист. Объект создается на стеке и при выходе за границы фигурных скобок вызывается деструктор и он перестает существовать, а все его содержимое будет перезаписано любой следующей переменной, размешенной на стеке. Вот код, о котором я говорю:
    case 1: {
          cout << endl << "Введите модель:";
          cin >> model;
          cout << endl << "Введите прозводителя:";
          cin >> producer;
          cout << endl << "Введите цену:";
          cin >> price;
          cout << endl << "Введите год выхода:";
          cin >> year;
          Appliances appliances{ model,producer,price,year };
          Appliances* pb = &appliances;
          catalog.push_back(pb);
          break;
        }


    Вот наглядный пример того что у вас получается.
    https://godbolt.org/z/bx5Wbo1bG

    Как исправить? Создавайте элемент в куче (new OhMyClass()). А лучше храните в std::list, например, std::unque_ptr
    Ответ написан
    2 комментария
  • Как правильно подключать библиотеку в CMake?

    @sergiodev
    Нужно ещё скомпоновать приложение (Core) с библиотеками, которые вы cоздали в подпапках:

    target_link_libraries(Core Window Out)

    после вызовов add_subdirectory().

    P.S. cmake_minimum_required(...) нужно по идее вызывать только в корневом CMakeLists.txt (где объявляется проект), во вложенных файлах это необязательно, насколько знаю.
    Ответ написан
    2 комментария