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

    @Mercury13
    Программист на «си с крестами» и не только
    Не сильно оптимизированный, но действующий код.
    CONST_STR = string_view или const string& в зависимости от версии Си.
    На выходе — кол-во замен.
    size_t str::replace(
            std::string& aString,
            const char aWhat,
            const char aByWhat)
    {
        size_t r = 0;
        FOR_S(i, 0, aString.length())
            if (aString[i] == aWhat) {
                aString[i] = aByWhat;
                ++r;
            }
        return r;
    }
    
    size_t str::replace(
            std::string &aString,
            char aWhat,
            CONST_STR aByWhat)
    {
        if (aByWhat.length()==1)
        {   // Simple replace
            return str::replace(aString, aWhat, aByWhat[0]);
        }
        // More complex replace
        static const size_t szWhat = 1;
        const size_t szByWhat = aByWhat.length();
        size_t p = 0, r = 0;
        while ((p = aString.find(aWhat, p)) != std::string::npos)
        {
            aString.replace(p, szWhat, aByWhat);
            p += szByWhat;
            ++r;
        }
        return r;
    }
    Ответ написан
    Комментировать
  • Где хранится nullptr? Для разных программ она разная?

    @Mercury13
    Программист на «си с крестами» и не только
    Что значит «хранится»?
    • Значение nullptr хранится в переменной-указателе — как и любое &someObject.
    • Объекта правильной «объектной» формы (если, конечно, у объекта присутствует какой-то инвариант или хотя бы таблица виртуальных методов) по адресу *nullptr нет; в большинстве ОС разыменование nullptr приводит к нарушению сегментации и мгновенному вылету (но не обязательно — например, DOS).
    • Во всех известных мне ABI (x86/DOS, x86 и x64/Windows, x86 и x64/Linux) nullptr равен нулевому адресу. Это обосновано — по этому адресу обычно располагается таблица прерываний, точка входа, префиксный сегмент или прочая системная хрѣнь, сюда точно соваться не стоит. Но теоретически не обязательно. Этот адрес, естественно, общий для ABI, чтобы можно было объединять в одну программу результаты компиляции разными языками.
    Ответ написан
    Комментировать
  • Возможно ли сделать шаблонную функцию дружественной классу?

    @Mercury13
    Программист на «си с крестами» и не только
    #include <iostream>
    
    class Wrap
    {
    public:
        Wrap(int x) : value(x) {}
    private:
        int value;
    
        template <class T>
        friend void out(const T&);
    };
    
    template <>
    void out(const Wrap& x)
        { std::cout << x.value << std::endl; }
    
    int main()
    {
        Wrap x(42);
        out(x);
        return 0;
    }


    Если нужно в пространстве имён…
    spoiler
    #include <iostream>
    
    namespace qq {
        template <class T>
        void out(const T&);
    }
    
    class Wrap
    {
    public:
        Wrap(int x) : value(x) {}
    private:
        int value;
    
        template <class T>
        friend void qq::out(const T&);
    };
    
    template <>
    void qq::out(const Wrap& x)
        { std::cout << x.value << std::endl; }
    
    int main()
    {
        Wrap x(42);
        qq::out(x);
        return 0;
    }
    Ответ написан
    Комментировать
  • Почему в C++ всё именно так?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Const-корректность придумал Бьярне Строуструп, и только ≈1999 её внесли в Си без крестов. А поскольку память, занятую строковым литералом, изменять запрещается (а в современных ОС литералы сидят в особом неизменяемом сегменте), понятное дело, новым типом для них стал const char*. Поскольку Си99 для совместимости позволял писать char* text = "foo";, а разработка Си(++) застопорилась на десятилетие, учебники по Си очень долго писали такую нерекомендуемую строчку.

    3. Для маленьких объектов это одно и то же, но для больших немалая разница. Эта программа компилируется начиная с Си++17.
    #include <iostream>
    
    class WrapInt
    {
    public:
        int value = 0;
        WrapInt(int x) : value(x) {}
        WrapInt(WrapInt&) = delete;
    private:
    };
    
    int main()
    {
        WrapInt x = 42;
    }


    int zero = 0; до C++14 — создать временный объект, вызвать конструктор перемещения, а остальное делает оптимизатор. С Си++17 — вызов конструктора, если он не explicit. Мне казалось, что в очень старых версиях DJGPP был задействован op=, но ХЕЗ, MinGW и Си++03 вызывают именно конструктор.

    int zero = int(0); — что удивительно, то же самое, только конструктор любой! Хотя выглядит как явное создание временного объекта.

    int zero(0); — вызов конструктора во всех версиях Си++.

    int zero{0}; — универсальный инициализатор Си++11, также вызов конструктора.

    4. int array[] = { 1, 2, 3}; — все версии Си и Си++. int array[] { 1, 2, 3}; — универсальный инициализатор Си++11.

    6. Эдсгер Дейкстра рассказал, почему массивы должны нумероваться с нуля, а целые диапазоны — задаваться в формате [a,b). Итак, у нас есть диапазон [a,b) и массив с запасом. Как сдвинуть b и добавить в диапазон один элемент? — array[b] = newElem; ++b;. Керниган и Ритчи решили объединить это чудо в одну строку array[b++] = newElem;, к тому же в процессорах действительно бывают пред- и постинкрементные операции.

    7. void — это псевдотип, чьи элементы создавать нельзя. Указатели можно передвигать с шагом, который равен размеру типа — то есть прибавляем 1, реально прибавляется 1, 2, 4, 8 или сколько там. Для void*, который является простым указателем на память, шаг 1 байт. Или 1 слово, если машина оперирует более крупными словами — такие были, но из-за переусложнённой работы с текстом от них отказались. Но инкременты-декременты void*, если и возможны, то нежелательны, и вот почему.
    Чтобы «соединить ежа с ужом» (длинное слово и доступ по байтам), x86 говорит: работа с невыровненными данными (например, 4 байта по адресу 21) будет происходить медленно и по неопределённому алгоритму. Itanium вообще, ЕМНИП, к невыровненным данным не обращается, как это устроено в компиляторах: char* → int* говорит, что обращаться нужно медленно и программно, void* → int* — быстро и аппаратно.

    8. libc можно и не подключать, хоть это и сложно. А return из main НЕ вызывает std::exit. Линкер создаёт код обвязки, который открывает все глобальные объекты, вызывает main), закрывает все глобальные объекты и выходит из программы по-системному.
    Ответ написан
    Комментировать
  • Что за скобки в выражении std::true_type{}?

    @Mercury13
    Программист на «си с крестами» и не только
    Универсальный инициализатор Си++11. В нём может быть что угодно: параметры конструктора, initializer_list, поля структуры… Ваш код эквивалентен коду
    std::string helloWorld(std::move(hello().operator std::string()));

    Вроде верно — раньше, НЯЗ, в helloWorld вызывался op= КОНСТРУКТОР КОПИРОВАНИЯ, а сейчас С СИ++17 конструктор.
    Для временных объектов вызывается версия string(string&&), я это обозначил как string(std::move()).
    Ну и для преобразования hello в string используется hello::op string.
    hello(), или, начиная с Си++11, hello{} — это создание временного объекта с помощью конструктора по умлочанию.
    Ответ написан
    Комментировать
  • Как решить ошибку "String is not null terminated"?

    @Mercury13
    Программист на «си с крестами» и не только
    Вы неправильно пользуетесь функцией strcat_s.
    Традиционно в Си (видимо, из-за особенностей машины PDP-11) каждая строчка заканчивается нулевым символом.
    Смысл strcat_s — если строка находится в буфере ограниченной длины и по какой-то причине не нуль-терминированная, функция не полезет в «левую» память, а ограничится размером буфера. Вторым параметром должна быть длина буфера, а не strlen, который обесценивает весь смысл суффикса «safe».
    Ответ написан
    Комментировать
  • Как поделить на о в С++?

    @Mercury13
    Программист на «си с крестами» и не только
    Деление на целочисленный 0 невозможно, происходит авария «деление на 0».

    В процессорах, которые делят 2w:w → w, эта авария происходит и в других случаях. Скажем, если слово — это две десятичных цифры, то 1000:2 даст ошибку: 500 не укладывается в эти две цифры. В Си++, который умеет делить только w/w, такое сделать можно только на ассемблере.

    Деление конечного на дробный 0.0 даёт ±∞ — особое «число», которое больше/меньше любого конечного.
    Деление 0.0/0.0 даёт NaN (not a number, не-число).

    Откуда вы взяли мантиссу 1,87235 — я не знаю. Минимальное денормализованное число в float 1.4e−45, в double 4.9e−324. Почему так мало знаков — да потому что погрешность у таких чисел ±100%: меньше только 0, а следующее вдвое больше.

    UPD. В редко используемом и очень медленном extended (= long double) есть что-то похожее — 1.9e−4951.
    Ответ написан
    Комментировать
  • Как уменьшить выходной exe при компиляции?

    @Mercury13
    Программист на «си с крестами» и не только
    Это вообще как компилируете?
    Если у вас MinGW и консольная программа, советую что-то вроде
    -O3 -Os -static-libgcc -static-libstdc++ -static -lpthread

    Получается самодостаточный EXE’шник, и для небольших программ ≈400K, для программ побольше — около 2М.
    Ответ написан
    Комментировать
  • Что обозначают записи: Class object(&anotherObject); и AnotherClass anotherObject(10);?

    @Mercury13
    Программист на «си с крестами» и не только
    1. Создать в стандартном месте (на стеке или в сегменте данных) объект object типа Class с вызовом конструктора Class::Class(AnotherObject*).
    2. Создать там же объект anotherObject типа AnotherClass с вызовом конструктора AnotherClass::AnotherClass(int).
    Ответ написан
    Комментировать
  • Почему один и тот же код на python и c++ дает разный результат?

    @Mercury13
    Программист на «си с крестами» и не только
    hypot в Си даёт гипотенузу, а a²+b² в змее-питоне — квадрат гипотенузы.
    Ответ написан
    Комментировать
  • Как заполнить map английскими буквами и вывести его?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что map — это функциональное соответствие. Одному ключу соответствует одно значение.
    Или используйте multimap. Или варьируйте ключ, а не делайте жёсткой единицей.
    Ответ написан
    Комментировать
  • Почему строка конвертируется в неправильное число?

    @Mercury13
    Программист на «си с крестами» и не только
    Ваш текст представляет собой число, превосходящее диапазон int. В таком случае ваша реализация atoi выводит максимум возможного.
    Можно использовать:
    • sscanf long long
    • atoll (C++11)
    • stoll (C++11)
    • from_chars (C++17)
    Ответ написан
    1 комментарий
  • Как сделать, чтобы конструктор при определённых условиях не компилировался — а не сваливал на runtime?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    Вот действующий код.
    Хоть он на 20, простейшими define’ами (consteval → constexpr, constinit → пустота) можно собрать его и на 17.
    #include <iostream>
    #include <array>
    #include <string>
    
    enum class FromRawMem { INST };
    enum class NoValue { INST };
    
    #define consteval constexpr
    
    
    class Char4
    {
    public:
        using CppArray = std::array<char, 4>;
        constexpr explicit Char4(NoValue) : fBuf { { 0, 0, 0, 0 } } {}
        consteval Char4(const char (&x)[5]) : fBuf { { x[0], x[1], x[2], x[3] } } {}
        constexpr Char4(const Char4&) = default;
        Char4(FromRawMem, const char* data) { fBuf.asInt = *reinterpret_cast<const uint32_t*>(data); }
        constexpr const CppArray& toAr() const { return fBuf.asAr; }
        CppArray& toAr () { return fBuf.asAr; }
        constexpr std::string_view toSv() const { return { fBuf.asAr.data(), 4 }; }
    
        /// @return  byte-order dependent integer value
        /// @warning
        ///    DO NOT save toRawInt() as Intel, Motorola or any numeral system (decimal etc).
        ///    You MAY save it as raw memory, or use it for optimization.
        constexpr uint32_t toRawInt() const { return fBuf.asInt; }
        constexpr const char* data() const { return fBuf.asAr.data(); }
    private:
        union {
            std::array<char, 4> asAr;
            uint32_t asInt;
        } fBuf;
    };
    
    constexpr inline bool operator == (Char4 x, Char4 y) { return x.toRawInt() == y.toRawInt(); }
    constexpr inline bool operator != (Char4 x, Char4 y) { return x.toRawInt() != y.toRawInt(); }
    constexpr inline bool operator == (Char4 x, std::string_view y)
            { return y.size() == 4 && x.toRawInt() == *reinterpret_cast<const uint32_t*>(y.data()); }
    constexpr inline bool operator != (Char4 x, std::string_view y)
            { return !operator == (x, y); }
    constexpr inline bool operator == (std::string_view x, Char4 y)
            { return operator == (y, x); }
    constexpr inline bool operator != (std::string_view x, Char4 y)
            { return !operator == (y, x); }
    
    
    /// Size of enum class, no generic implementation.
    template <class Ec>
    constexpr size_t enumSize();
    
    namespace detail {
        /// Jut does not compile if arguments contain assignments
        constexpr int enumDummy(...) { return 0; }
    
        template <typename T, std::size_t...Is>
        consteval std::array<T, sizeof...(Is)>
        make_array(const T& value, std::index_sequence<Is...>)
        {
            return {{(static_cast<void>(Is), value)...}};
        }
    
        template <std::size_t N, typename T>
        consteval std::array<T, N> make_array(const T& value)
        {
            return make_array(value, std::make_index_sequence<N>());
        }
    }
    
    #define DEFINE_ENUM_SIZE(Name) \
        template <> constexpr size_t enumSize<Name>() { return static_cast<int>(Name::NN); }
    
    #define DEFINE_ENUM_N(Name, ...)  \
        enum class Name { __VA_ARGS__ };   \
        template <> constexpr size_t enumSize<Name>() { \
            enum Internal { __VA_ARGS__ , NNNNNN };  \
            detail::enumDummy(__VA_ARGS__);  \
            return NNNNNN; }
    
    
    enum class DummyElem { INST };
    enum class IncompleteArray { INST };
    enum class FillArray { INST };
    enum class PairwiseTempInit {INST };
    
    
    namespace detail {
        namespace array {
    
            template <auto K, class V>
            class KeyValue {
            public:
                static constexpr auto index = K;
                V && v;
                consteval KeyValue(V && aV) : v(aV) {}
            };
    
            // checkOneForRepeat
            template <class Ec>
            consteval void checkOneForRepeat() {}
    
            template <class Ec, Ec Only>
            consteval void checkOneForRepeat() {}
    
            template <class Ec, Ec First, Ec Second, Ec ... Rest>
            consteval void checkOneForRepeat()
            {
                static_assert(First != Second, "Repeating array keys!");
                checkOneForRepeat<Ec, First, Rest...>();
            }
    
            // checkForRepeat
            template <class Ec>
            consteval void checkForRepeat() {}
    
            template <class Ec, Ec Only>
            consteval void checkForRepeat() {}
    
            template <class Ec, Ec First, Ec Second, Ec ... Rest>
            consteval void checkForRepeat()
            {
                checkOneForRepeat<Ec, First, Second, Rest...>();
                checkForRepeat<Ec, Second, Rest...>();
            }
    
            template <class Ec, Ec ... args>
            consteval void checkKeys()
            {
                checkForRepeat<Ec, args...>();
            }
    
        }
    }
    
    template<auto K, class V>
    consteval auto kv(V && v) { return detail::array::KeyValue<K, V>(v); }
    
    
    template <class T, class Ec>
    class EcArray
    {
    public:
        static constexpr auto Size = enumSize<Ec>();
        using CppArray = std::array<T, Size>;
        using Elem = T;
        using iterator = Elem*;
        using const_iterator = const Elem*;
        constexpr CppArray& toAr() { return fBuf; }
        constexpr const CppArray& toAr() const { return fBuf; }
        constexpr Elem* data() { return fBuf.data(); }
        constexpr const Elem* data() const { return fBuf.data(); }
        constexpr size_t size() const { return Size; }
        constexpr iterator begin() { return fBuf.begin(); }
        constexpr iterator end() { return fBuf.end(); }
        constexpr const_iterator begin() const { return fBuf.begin(); }
        constexpr const_iterator end() const { return fBuf.end(); }
        constexpr const_iterator cbegin() const { return fBuf.begin(); }
        constexpr const_iterator cend() const { return fBuf.end(); }
    
        constexpr EcArray() = delete;
        constexpr EcArray(NoValue) {}
        constexpr EcArray(const EcArray&) = default;
        constexpr EcArray(EcArray&&) = default;
    
        EcArray& operator = (const EcArray&) = default;
        EcArray& operator = (EcArray&&) = default;
    
        template <class U>
        constexpr EcArray(NoValue, const U&& x) : fBuf {x } {}
    
        template <class ... Args>
        consteval EcArray(Args&& ... x)
            : fBuf { x... }
            { static_assert(sizeof...(Args) == Size, "EcArray size mismatch"); }
    
        template <class U, Ec K, class V, class ... Args>
        consteval EcArray(
                PairwiseTempInit, U&& temp,
                detail::array::KeyValue<K, V> first,
                Args&& ... rest)
            : fBuf { detail::make_array<Size, T>(temp) }
        {
            static_assert(sizeof...(Args) + 1 == Size, "EcArray pairwise size mismatch");
            detail::array::checkKeys< Ec, K, (std::remove_reference_t<decltype(rest)>::index)... >();
            initPairwise(first, rest...);
        }
    
    private:
        CppArray fBuf;
    
        consteval void initPairwise() {}
    
        template <Ec K, class V, class ... Args>
        consteval void initPairwise(
                detail::array::KeyValue<K, V> first,
                Args&& ... rest)
        {
            fBuf[static_cast<size_t>(K)] = T { first.v };
            initPairwise(rest...);
        }
    };
    
    
    DEFINE_ENUM_N( Letter, A, B, C )
    
    extern const EcArray <Char4, Letter> names2;
    
    //const EcArray <Char4, Letter> names { "alph", "brav", "char" };
    
    constinit const EcArray <Char4, Letter> names2
        { PairwiseTempInit::INST, Char4 { NoValue::INST},
          kv <Letter::A> ( "alp1" ),
          kv <Letter::B> ( "bra1" ),
          kv <Letter::C> ( "cha1" ) };
    
    
    int main()
    {
        std::cout << "Hello World!" << std::endl;
        return 0;
    }
    Ответ написан
    Комментировать
  • Как создать проект с++ на основе готового кода?

    @Mercury13
    Программист на «си с крестами» и не только
    У меня завелось сразу, хоть и с предупреждениями. Разумеется, проект приходится делать своими силами — и раз уж я использую Qt, сделал вот такой. Прокомментирую его по строчке.
    # Эти четыре строчки Qt установил сам, когда я через интерфейс сказал: нужен проект без Qt.
    # Я только заменил C++11 на 17, едва увидел make_unique.
    TEMPLATE = app
    CONFIG += console c++17
    CONFIG -= app_bundle
    CONFIG -= qt
    
    # Минимальная программа на MinGW требует трёх DLL’ек,
    # и для простоты развёртывания я прикрыл две из них.
    QMAKE_CXXFLAGS += -static-libgcc -static-libstdc++
    QMAKE_LFLAGS += -static-libgcc -static-libstdc++
    
    # Установлено через интерфейс
    SOURCES += \
        olcExampleProgram.cpp
    
    HEADERS += \
        olcPixelGameEngine.h
    
    # Как только дошло до ошибок линкера, я стал смотреть, чего не хватает.
    # А не хватает многого.
    LIBS += -lopengl32 -lgdi32 -lgdiplus -ldwmapi -lshlwapi
    
    # Это третья DLL’ка, которая нужна (была) минимальному проекту на MinGW.
    LIBS += -static -lpthread


    К сожалению, это преимущество Си и бич Си++ — сборка файлов в проект идёт неязыковыми мерами, и компиляторы Си первыми появлялись на машинах, где нет ничего, кроме ассемблерных утилит (линкера, библиотекаря). Систем сборки в мире даже больше, чем компиляторов.
    Ответ написан
    Комментировать
  • Как избавиться от явного задания типа в вариативном шаблоне (двухмерный массив, инициализируемый строчками)?

    @Mercury13 Автор вопроса
    Программист на «си с крестами» и не только
    #include <iostream>
    #include <array>
    
    #define consteval constexpr
    
    class Char4
    {
    public:
        using Array = char[5];
        using CppArray = std::array<char, 5>;
        consteval Char4(const char (&x)[5]) : fData { x[0], x[1], x[2], x[3] } {}
        constexpr std::string_view toSv() const { return { fData.asAr, 4 }; }
    private:
        union Ch {
            struct asInts {
                char a, b, c, d;
            };
            char asAr[4];
        } fData;
    };
    
    template <size_t N>
    class CArray4
    {
    public:
        using CppArray = const std::array<Char4, N>;
        constexpr size_t size() const { return N; }
        const Char4& operator [] (size_t i) const { return fBuf[i]; }
    
        template <class ... Args>
        consteval CArray4(Args&& ... x)
            : fBuf { x... }
        {
            static_assert(sizeof...(Args) == N, "CArray size mismatch");
        }
    private:
        CppArray fBuf;
    };
    
    
    namespace {
        CArray4<3> names { "alph", "brav", "char" };
    }
    
    
    int main()
    {
        std::cout << names.size() << std::endl;
        std::cout << names[2].toSv() << std::endl;
        return 0;
    }
    Ответ написан
    Комментировать
  • Что лучше unsigned int или long void?

    @Mercury13
    Программист на «си с крестами» и не только
    Что лучше: сóрок пя́ток или пятóк сорóк? © Винни-Пух
    unsigned int — это тип, поддерживающий положительные числа минимум до 65535 (а в современных реалиях — до 4 млрд)
    long void — нет такого типа в Си++.

    Да, я догадываюсь, вы хотите разницу между unsigned int и unsigned long. Unsigned long поддерживает не менее 4 млрд и в большинстве соглашений соответствует unsigned int (но иногда может давать 1,8·1019 — например, в Unix).

    Если вы пишете под конкретный процессор и конкретную ОС — пишите как угодно.

    Для простейшей кроссплатформенности x86/x64 Windows/Mac/Unix я вообще не рекомендую использовать long, используя int и long long. Для индексов в массиве (и прочего добра, чья длина зависит от длины процессора) используйте size_t и ptrdiff_t.

    Для широкой кроссплатформенности лучше использовать (u)int32_t (если важна арифметика переполнений или точное количество байтов), (u)int_fast32_t, (u)int_least32_t (если важно, чтобы хранило не менее N байтов).
    Ответ написан
    Комментировать
  • Инициализация элемента к нулю?

    @Mercury13
    Программист на «си с крестами» и не только
    1, 3. Просто совпадение, обычно потому, что компиляторы в отладочном режиме всё инициализируют нулями (проще отлаживать). На это нельзя рассчитывать, пиши HWND window = nullptr.
    2. Лучше написать std::string str;, да и всё. Можно рассчитывать, что будет пустая строка.

    Почему 1,3 плохо, а 2 хорошо? Потому что HWND и HRESULT — синонимы для встроенных типов (не то указатель, не то число того же размера), а у них конструктор по умолчанию ничего не делает. А string — нормальный себе объект.
    Ответ написан
    4 комментария
  • Как решить даную проблему?

    @Mercury13
    Программист на «си с крестами» и не только
    Тут всё просто: вы попытались разыменовать NULL-указатель.
    Надо хоть проверить, что наша нестандартная функция (с КиберФорума?) вернула.
    Ответ написан
    Комментировать
  • Почему выполнение программы ускоряется?

    @Mercury13
    Программист на «си с крестами» и не только
    Потому что у вас неэффективный алгоритм вычисления НОД, работающий на вычитании, а не на делении с остатком.
    Естественно, НОД(1,6) = НОД(1, 6−1=5) = НОД(1, 5−1=4) = НОД(1, 4−1=3) = НОД(1, 3−1=2) = НОД(1, 2−1=1) = НОД(1, 1−1=0) = 1
    НОД(2,6) = НОД(2, 6−2=4) = НОД(2, 4−2=2) = НОД(2, 2−2=0) = 2
    С арифметическим переполнением никак не связано. Просто даже в результате переполнения получились немаленькие числа.

    Как надо: НОД(1,6) = НОД(6, 1%6=1) = НОД(1, 6%1=0) = 1
    Аналогично для НОД(6,2) — в общем, сходится довольно быстро.
    Ответ написан
    Комментировать