1. C++ / Говнокод #24612

    +2

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    #include <iostream>
    #include <string>
    
    int main()
    {
        std::string s1  = "";
        const char s2[] = "";
    
        std::cout << std::boolalpha
                  << std::empty(s1) << std::endl
                  << std::size(s1) << std::endl
                  << std::empty(s2) << std::endl
                  << std::size(s2) << std::endl;
    
        s1.assign("", 1);
        std::cout << std::empty(s1) << std::endl
                  << std::size(s1) << std::endl;
        system("pause");
        return 0;
    }

    true
    0
    false
    1
    false
    1


    Ой-вэй, абстракции потекли!

    Запостил: gost, 11 Августа 2018

    Комментарии (101) RSS

    • Побежал за тряпками.
      Ответить
    • показать все, что скрытоvanished
      Ответить
      • Не. Это проблемы от напяливания крестов на сишку.

        У std::string есть поле с длиной и она может хранить нолики посреди строки.

        Но строковые литералы в языке остались сишные, поэтому "" это в реальности массив из одного '\0'. А у std::string в конструкторе с одним параметром есть костыль -- копируются только символы до первого нолика. Иначе юзать std::string было бы неудобно.

        В строке 15 заюзали другой конструктор, в который длина передаётся явно и получился std::string, который содержит один символ '\0'.
        Ответить
        • А собственно зачем в C++ придумали столько однотипной херни: std::string, std::vector, std::array?
          Нет чтоб сделать единый контейнер для подобного говна, и добавить б там каких-нибудь конструкторов, ну чтоб можно было там способ аллокации выбирать (если говорить про std::vector и std::array чтоб каким-то там параметром передавалось то, хотим ли мы статический массив или рантайм-вектор)
          Ответить
          • Полагаю, что во времена, когда придумывали std::vector и std::array, реализовать подобное было как минимум затруднительно, т.к. такой обильной метушни в крестах ещё не было.
            Ну а в "Modern C++" это вообще как-то некуда впихивать, в том числе из-за подобного сахара:
            auto pituh = std::vector{ 1, 2, 3 };


            UPD: а для строк так вообще нужны специфические методы, типа того же find'а, split, join... - в интерфейсе вектора это всё нинужно.
            Ответить
            • > UPD: а для строк так вообще нужны специфические методы, типа того же find'а ... - в интерфейсе вектора это всё нинужно.

              Да ладно, а если мне надо последовательность из четырех флоат чисел в векторе из флоатов найти?
              Ответить
              • Я постулировал наличие методов, которые имеют какое-то практическое применение исключительно для строки. Какие именно это методы - вопрос для длительного обсуждения профессиональными обсуждателями, но, к примеру, split/join для не-строк имеют сомнительную ценность (хотя и могут как-нибудь использоваться, да), а upper/lower/swapcase - вообще не имеют смысла. С find'ом - да, поторопился.
                Ну а то, что цэпэпэшная "строка" - говно несусветное, известно уже давно и, похоже, всем.
                Ответить
                • > а upper/lower/swapcase - вообще не имеют смысла.

                  upper/lower/swapcase должны быть не методами контейнера, а методами для типов, всунутых в контейнер (контейнеру незачем знать, что за говно в нем хранится). Ну типа можно тупым циклом или через std::for_each пробежать вектор или строку, и чтоб можно было и upper/lower/swapcase к каждому сделать (что имеет смысл для "букв") и для каждого числа abs() сделать например (что имеет смысл для знаковых чисел, но не имеет смысла для беззнаковых) или возвести в квадрат (что имеет смысл и для знаковых и для беззнаковых).

                  Собственно к чему я? А, да. Вот на кой хер собственно делать специальный контейнер для именно массива из "букв"? Только чтоб нолик дописать? Делать upper/lower/swapcase методом самого контейнера? Тьфу! Давайте уж тогда сделаем контейнеры отдельно для беззнаковых и знаковых чисел, и тогда только в контейнере для знаковых будет abs(), ну не охуенно ли?
                  Ответить
                  • > на кой хер собственно делать специальный контейнер для именно массива из "букв"?

                    Потому что компьютер ничего не знает о "буквах". Компьютер знает о байтах. Если тебе нужны "буквы", то контейнер для них нужно аккуратно спроектировать (std::string не является хорошим примером).

                    Один из наилучших и наиболее компактных способов — хранить строки как иммутабельные массивы байтов, представляющие кодпоинты, заэнкоженые в UTF-8.

                    Только сырые байты из такой строки торчать не должны. to_upper / to_lower тоже специально делать не нужно, достаточно итераторов, которые перебирают кодпоинты, и общих оптимизированных функций вида
                    map : string → f:(code_point → code_point) → string
                    map_concat : string → f:(code_point → string) → string
                    to_upper/to_lower/swapcase и прочие операции можно выразить тривиально как
                    map CodePoint.to_upper my_string
                    Модифицировать utf-8 строки in-place в общем случае нельзя (функция transliterate может менять размер контейнера).

                    Подытожим:
                    • специальный контейнер для строк — отличная идея.
                    • реализация std::string в плюсах — говно, потому что UTF-8 тогда ещё не придумали, и юникод был не так повсеместен.
                    Ответить
                    • >Один из наилучших и наиболее компактных способов — хранить строки как иммутабельные массивы байтов, представляющие кодпоинты, заэнкоженые в UTF-8.

                      Лучше хранить в UTF-32 - так мы избавимся от питушни, связанной с посимвольным проматыванием в итераторах, и все сведется к обычному uint32_t массиву, который можно напрямую индексировать.

                      Ну а если у нас мало оперативки, можно конечно и UTF-8. Только вот что насчет UTF-16, которая тоже с переменной длиной символов? А есть еще китайская кодировка GB 18030, где тоже та же хрень. К тому же в подобной хуйне в теории можно было б хранить последовательность инструкции процессора, если процессор может в инструкции переменной длины (как например в x86) чтоб их проматывать через дизассемблер длин. По-моему было б логичнее просто сделать иммутабельный кусок байт, и сделать типа специализацию чтоб туда мог приделываться итератор для проматывания UTF-8, UTF-16, GB 18030, машинного кода x86 или любой другой подобной хуйни, чем делать по контейнеру для любой хрени переменного размера в котором размер каждой хрени можно посчитать на основе значения этой самой хрени (как в UTF-8, UTF-16, GB 18030, x86 машкода и проч...)
                      Ответить
                      • > Лучше хранить в UTF-32

                        Иногда лучше, иногда нет. Сконвертировать всегда можно при необходимости, это тривиально при наличии итератора.

                        > что насчет UTF-16

                        нинужен

                        > Ну а если у нас мало оперативки, можно конечно и UTF-8.

                        UTF-8 лучше не только для оперативки, но и для пирфоманса. Так лучше small-string optimization работать будет, больше полезных данных в кэш помещаться будет.

                        Более того, в оперативке твои строки никому не нужны, когда ты будешь какой-нибудь HTML отдавать, всё равно в UTF-8 конвертировать придётся, так зачем конвертить?

                        > По-моему было б логичнее просто сделать иммутабельный кусок байт

                        Кто будет помнить кодировку, царь? Пусть уж из типа будет понятно, в какой кодировке там всё лежит.
                        Ответить
                        • > UTF-8 лучше не только для оперативки, но и для пирфоманса.

                          Далеко не факт. В UTF-8 найти какое-нибудь слово будет сложнее, некоторые алгоритмы поиска подстроки работают довольно хитровыебаным способом, и необходимость раздрачивать всю последовательность UTF-8 чтоб получить какой-то там символ в N-ной позиции делает эти алгоритмы попросту непрактичными, и приходится использовать более хуевые алгоритмы. То же самое касается заматчивания регулярок. UTF-8 очень хуев, когда в нем надо что-то искать.

                          > Кто будет помнить кодировку, царь? Пусть уж из типа будет понятно, в какой кодировке там всё лежит.

                          Да без проблем
                          typedef immutabelny_kusok_byteov utf8_string

                          и потом делать функции, которые принимают utf8_string в качестве аргумента и что-то там с ними делают, а если пихать этим функциям immutabelny_kusok_byteov то будет варнинги или вообще не скомпилируется
                          Ответить
                          • > будет варнинги или вообще не скомпилируется

                            Нет, тайпдеф не так работает.
                            Ответить
                            • > Нет, тайпдеф не так работает.

                              Я знаю. Но наверняка можно сделать чтоб работал (запатчить в компиляторе хрень какую-нибудь). Или тупо в struct {} обернуть.
                              Ответить
                          • > utf-8 очень хуёв...
                            ... когда в нём надо что-то менять.

                            А искать нормально должно. Откатываться на запомненные позиции легко даже с utf-8.
                            Ответить
                          • З.Ы. Ну и поскольку представление кодепоинтов в utf-8 однозначно, можешь тупо искать последовательность байт.

                            С регулярками сложнее, но тоже можно конпелять автомат так, чтобы он прямо над байтами работал.
                            Ответить
                            • показать все, что скрытоvanished
                              Ответить
                              • > в смещение в символах
                                А оно надо? Подстрока и с байтовыми оффсетами нормально выдернется.

                                З.Ы. Большинству алгоритмов от строки нужны только итераторы, а не произвольный доступ к символам.
                                Ответить
                            • Ну вот допустим тебе надо найти слово по шаблону "a???????b" где "?" это что угодно кроме {'\n', '\t', ' '}, т.е. в UTF-8 эти вопросики могут быть каким-то многобайтным юникодовым говном. В кодировке с фиксированным размером символом ты проверяешь сначала первую букву что это 'a' - тогда проверяешь последнюю что это 'b' - и теперь осталось проверить только то, что в этих вопросительных знаках нет {'\n', '\t', ' '}. Если у тебя говноUTF-8, такая хуйня не сработает, ты не сможешь прыгнуть на ту последнюю букву, не пройдя всю цепочку
                              Ответить
                              • > осталось проверить только то, что там нет в этих вопросительных знаках нет {'\n', '\t', ' '}
                                Ну т.е. один хуй пробежать по всем символам?
                                Ответить
                                • Только тут это можно сделать каким-нибудь SSE, а в UTF-8 надо каждый символ дрочить
                                  Ответить
                                • А если просто надо найти "a????.....???b" т.е. найти в тексте место, где расстояние от буквы 'a' до 'b' равно столько-то символов, то и пробегать не надо. Видим букву 'a' - смотрим, если ли вон там буква 'b'.
                                  Ответить
                                  • ну т.е. ты взаправду сидишь и пытаешься найти недостатки универсального транспортного кодирования текста, ну ок, возможно, ты прав, искать ровное расстояние между а и b в символах на миллиарде семплов - это то, ради чего не стоит брать utf-8
                                    только ведь и UCS-32 тебе не поможет закодировать залго так, чтобы 4 * число букв совпало с числом байт (проверь)
                                    что порекомендуешь? UCS-256? UCS-1024?
                                    Ответить
                                    • > что порекомендуешь
                                      Массив из указателей на символы.
                                      Ответить
                                      • Двусвязный список из небольших массивов в которых будут указатели на символы.
                                        Ответить
                  • > ну не охуенно ли
                    Ну вот в numpy или матлабе так и поступили: сделали контейнеры для чисел с арифметическими операциями. Поэтому учоные пользуются ими, а не сишечкой. А логи парсят питоном и перлом, а не сишечкой.
                    Потому что если тебе надо каждый день преобразовывать строки в верхний регистр, то удобнее для этого иметь одну функцию, а не for_each+toupper.
                    Ответить
            • > std::string
              > строка
              Если бы не костыль с c_str(), то эту хуету с чистой совестью можно было бы назвать ByteArray. Там больше ни одного специфичного для строк метода нету.
              Ответить
              • Мне кажется, ты упускаешь из виду нинужную оверинженерную питушню с char_traits

                Насколько я помню, основное отличие строки от вектора в том, что содержимое строк должно быть всегда тривиально копируемо memcpy, якобы это позволяло сделать более простую и эффективную реализацию, но пруфов сходу не найду.
                Ответить
                • > всегда тривиально копируемо memcpy
                  А для вектора из POD'ов подобная оптимизация нинужна?
                  Ответить
                  • А до одиннадцатого стандарта был простой способ написать специализацию для POD-ов?
                    Ответить
                    • Ну для примитивов и структурок, которые юзер пирфоманса ради явно пометит как POD trivially copyable -- с древних времён можно было, кмк. Этого бы уже хватило для большинства применений.
                      Ответить
                • И всё-таки, как мне кажется, основное отличие "строки" от вектора -- терминирующий нолик, прикостыленный для совместимости с няшной.
                  Ответить
              • А в Qt наоборот - QByteArray содержит функции для работы с однобайтовыми строками (юникодная строка - QString). И всегда* на всякий случай дописывает ноль в конце, чтобы можно было вызвать constData() и получить сишную строку.

                --
                * за исключением случая, когда QByteArray инициализируется константными данными без копирования. Если возможна такая ситуация, лучше использовать data(), который при необходимости нолик допишет.
                Ответить
                • В общем-то, в крестах c_str() и data() у std::string тоже обязаны возвращать нуль-терминированную строку:
                  const_reference operator[](size_type pos) const;
                  reference operator[](size_type pos);
                  <...>
                  Returns: *(begin() + pos) if pos < size(). Otherwise, returns a reference to an object of type
                  charT with value charT(), where modifying the object to any value other than charT() leads to
                  undefined behavior.

                  § 24.3.2.5 (N4659)

                  const charT* c_str() const noexcept;
                  const charT* data() const noexcept;
                  <...>
                  Returns: A pointer p such that p + i == &operator[](i) for each i in [0, size()].

                  § 24.3.2.7.1 (N4659)

                  Диапазон [0, size()] - закрытый, поэтому последний элемент у массива, возвращаемого data/c_str - charT(), что для std::string эквивалентно char() - то есть 0.
                  Ответить
                  • Блядь, как всё сложно. Поэтому я за "PHP".
                    Ответить
                  • Ничего не понял. Подставим во второе выражение i == size():
                    A pointer p such that p + size() == &operator[](size())

                    Раскрываем reference operator[](size_type pos). Поскольку !(pos < size()), это будет:
                    Otherwise, returns a reference to an object of type
                    charT with value charT(), where modifying the object to any value other than charT() leads to
                    undefined behavior.

                    А, так оно вернет ссылку на терминатор. Все ясно.
                    Хитро сформулировано. А то я из описания operator[] решил, что можно было бы не хранить \0, а возвращать ссылку на какой-то статический нолик...
                    Ответить
          • Ах да, и ещё один чрезвычайно важный момент:
            std::cout << std::is_pod_v<std::vector<int>> << std::endl   // false
                      << std::is_pod_v<std::array<int, 100>> << std::endl;  // true
            Ответить
          • > зачем в C++ придумали столько однотипной херни
            Чтобы ты заебался всё это учить. Ты ещё спроси, почему вместо std::vector<bool> получается какая-то невменяемая хуйня с битоёбством.
            Ответить
            • > с битоёбством

              Упаковка что ли? А как тогда указатель на элемент брать?
              Ответить
              • Не нужен тебе указатель, возьми лучше меня, желательно, в рот.
                Ответить
      • > две пустых строки
        З.Ы. Ну да, ты прав. Первая -- крестовая std::string с длиной ноль. И вторая, унаследованная от сишечки -- массив из 1 байта со значением '\0'.
        Ответить
        • показать все, что скрытоvanished
          Ответить
          • показать все, что скрытоvanished
            Ответить
            • > как кусок памяти
              Ты про const char s2[]? Ну да, это массив инициализированный сишным строковым литералом "" (т.е. одним байтом со значением '\0'). std::size() обработало его как и любой другой массив.
              Ответить
              • Объясните питонийцу, в чём отличие C++ртанских стринговых size и length.
                Ответить
                • Ну для сишной "строки" size == length + 1 (учли терминирующий нолик). А в остальных местах они однохуйственны.

                  З.Ы. Ещё не стоит путать size и sizeof. sizeof -- размер в байтах, size -- количество элементов.
                  Ответить
                  • >>>"терминирующий"

                    Типа умный?
                    Ответить
                  • Что за size? В сишке++ строка это объект?
                    Ответить
                    • Тут имелась в виду функция std::size() которая работает как на объектах-контейнерах в духе std::string так и на простых массивах.
                      Ответить
                      • так в рунтиме же есть вроде strlen?
                        Ответить
                        • Есть.
                          sizeof("hui") == 4
                          strlen("hui") == 3
                          std::size("hui") == 4
                          std::string("hui").size() == 3
                          std::string("hui").length() == 3
                          
                          sizeof(L"hui") == 16 (или 8)
                          wcslen(L"hui") == 3
                          std::size(L"hui") == 4
                          std::wstring(L"hui").size() == 3
                          std::wstring(L"hui").length() == 3
                          Ответить
                          • показать все, что скрытоvanished
                            Ответить
                          • показать все, что скрытоvanished
                            Ответить
                            • показать все, что скрытоvanished
                              Ответить
                              • > Чем можно заменить std::size в более старых компиляторах?
                                Метушнёй со SFINAE.
                                Ответить
                              • Что-то вроде такого (осторожно, мета-говнокод):
                                template<class T>
                                constexpr decltype(std::declval<T>().size()) MySize(const T & obj) { return obj.size(); }
                                
                                template<class T, size_t N>
                                constexpr size_t MySize(const T(&)[N]) { return N; };


                                Или, если хочется выебнуться получить сообщение об ошибке:
                                template<class T>
                                struct has_size {
                                private:
                                    struct NotChar { char x[2]; };
                                    template<class C> static char test(decltype(&C::size));
                                    template<class C> static NotChar test(...);
                                public:
                                    enum { value = sizeof(test<T>(0)) == sizeof(char) };
                                };
                                
                                template<class T>
                                constexpr typename std::enable_if<has_size<T>::value, decltype(std::declval<T>().size())>::type
                                    MySize(const T & obj) { return obj.size(); }
                                
                                template<class T>
                                constexpr typename std::enable_if<!has_size<T>::value, size_t>::type
                                    MySize(const T & obj) { static_assert(false, "fuck your T"); }
                                
                                template<class T, size_t N>
                                constexpr size_t MySize(const T (&)[N]) { return N; };
                                Ответить
                                • показать все, что скрытоvanished
                                  Ответить
                                  • > Borland и Digital Mars считают, что "hui" и L"hui" — это не массивы, а указатели, поэтому не могут определить размер
                                    Ну, это их проблемы:
                                    Ordinary string literals and UTF-8 string literals are also referred to as narrow
                                    string literals. A narrow string literal has type “array of n const char”, where n is the size of
                                    the string as defined below, and has static storage duration (6.7).

                                    § 5.13.5, 8 (N4659).

                                    С Watcom странно, видимо, он тоже считает строковые литералы указателями.
                                    Ответить
                                • показать все, что скрытоvanished
                                  Ответить
                                • показать все, что скрытоvanished
                                  Ответить
                                • Метушок, как реализовать "std::is_same" самому?
                                  Ответить
                                  • Довольно тривиально:
                                    template<class T1, class T2>
                                    struct MyIsSame {
                                        enum { value = false };
                                    };
                                    
                                    template<class T1>
                                    struct MyIsSame<T1, T1> {
                                        enum { value = true };
                                    };
                                    
                                    // ...
                                    using Int = int;
                                    typedef int Int2;
                                    std::cout << std::is_same<int, Int>::value << std::endl   // true
                                        << std::is_same<int, Int2>::value << std::endl   // true
                                        << std::is_same<Int2, long long>::value << std::endl  // false
                                        << MyIsSame<int, Int>::value << std::endl   // 1
                                        << MyIsSame<int, Int2>::value << std::endl   // 1
                                        << MyIsSame<Int2, long long>::value << std::endl;   // 0
                                    Ответить
                                    • показать все, что скрытоvanished
                                      Ответить
                                      • Вкратце, оно берёт более специализированный. В данном случае второй шаблон более специализированный, чем первый (основной), поэтому если типы подходят для второго, то он и будет использоваться.
                                        Читать далее: https://en.cppreference.com/w/cpp/language/partial_specialization, раздел "Partial ordering".
                                        Ответить
                            • > 28
                              Это что там такого MSVC в std::string накидала? Указатель на начало (8), размер (8) - а ещё что?
                              И, главное, как шланг смог в 4 байта?
                              P.S. Лол, на моей визуалке std::string весит 32 байта в релизном x64 режиме и 40 в отладочном. Ну и жирнота...
                              Ответить
                          • Одно из краеугольных отличий ГК от всех остальных кодерских ресурсов: использование метапеременных, ниспосланных нам Царём pituh/hui/kokoko вместо еретических богопротивных foo/bar/quux .
                            Ответить
                            • Но это не прибавляет ему элитарности; напротив - сайт выглядит днищнее любого другого форума по программированию. Впрочем, псевдоинтеллектуальная слащавость "Хабра" мне также не по нраву.
                              Ответить
                              • В порядке копронаблюдений.

                                >сайт выглядит днищнее любого другого форума по программированию
                                http://risovach.ru/upload/2013/05/mem/neytan_20159986_orig_.jpeg
                                Ответить
                              • > днищнее
                                Андеграунд, хули.
                                Ответить
    • Переведи на "Scratch 1.4".
      Ответить

    Добавить комментарий