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

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

    • Побежал за тряпками.
      Ответить
    • То есть в крестах две пустые строки: одна размером ноль байт, другая — размером 1 байт, состоящая из '\0'?
      Ответить
      • Не. Это проблемы от напяливания крестов на сишку.

        У 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'ом - да, поторопился.
                Ну а то, что цэпэпэшная "строка" - говно несусветное, известно уже давно и, похоже, всем.
                Ответить
            • > 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'.
        Ответить
        • Почему вторая захватила \0 включительно?
          Ответить
          • Вернее, почему std::size вернул 1? Он рассматривает это как кусок памяти, а не строку?
            Ответить
            • > как кусок памяти
              Ты про 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
                          Ответить
                          • А как поживает mbstring?
                            Ответить
                          • 1. std::size, вероятно, есть только в C++17 (лень проверять). gcc при указании более старой версии стандарта отказывается компилировать.

                            2. Просто оставлю это здесь:
                            gcc  Watcom Borland DMC MSVC clang
                            ----------------------------------------------------------------
                            sizeof("hui"):                 4      4      4     4    4    4
                            strlen("hui"):                 3      3      3     3    3    3
                            std::size("hui"):              4                             4
                            ----------------------------------------------------------------
                            sizeof(std::string("hui")):   24     16     16    12   28    4
                            std::string("hui").size():     3      3            3    3    3
                            std::string("hui").length():   3      3      3     3    3    3
                            ----------------------------------------------------------------
                            sizeof(L"hui"):                8      8      8     8    8    8
                            wcslen(L"hui"):                3      3      3     3    3    3
                            std::size(L"hui"):             4                             4
                            ----------------------------------------------------------------
                            sizeof(std::wstring(L"hui")): 24     16     16    12   28    4
                            std::wstring(L"hui").size():   3      3      3     3    3    3
                            std::wstring(L"hui").length(): 3      3      3     3    3    3
                            Ответить
                            • Так и есть, std::size появился в C++17: https://en.cppreference.com/w/cpp/iterator/size

                              Если у объекта есть метод size, то он возвращает его. Если же объявлен массив, возвращается его размер.

                              Чем можно заменить std::size в более старых компиляторах?
                              Ответить
                              • > Чем можно заменить 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; };
                                Ответить
                                • Для MSVC помогло. MySize("hui") == MySize(L"hui") == 4.
                                  Borland и Digital Mars считают, что "hui" и L"hui" — это не массивы, а указатели, поэтому не могут определить размер:
                                  Could not find a match for 'MySize<T,N>(char *)' in function main()
                                  Error: no match for function 'MySize(char const *)'


                                  Watcom вообще сообщает что-то непонятное:
                                  Error! E189: col(44) overloaded function cannot be selected for arguments used in call
                                  Note! N652: col(44) rejected function template: unsigned MySize( ?1 const (& )[1])
                                  Ответить
                                  • > 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 странно, видимо, он тоже считает строковые литералы указателями.
                                    Ответить
                                    • При этом Digital Mars не может посчитать &"hui":
                                      Error: can't take address of register, bit field, constant or string

                                      Watcom может.
                                      Ответить
                                • Слушай сюда, метушок со SFINAE. Метушкам не место на говнокоде!
                                  Ответить
                                • я бы советовал static_assert заменить на assert
                                  Ответить
                                • Метушок, как реализовать "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
                                    Ответить
                                    • Если несколько подходящих шаблонов, оно берет последний?
                                      Ответить
                                      • Вкратце, оно берёт более специализированный. В данном случае второй шаблон более специализированный, чем первый (основной), поэтому если типы подходят для второго, то он и будет использоваться.
                                        Читать далее: 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 в отладочном. Ну и жирнота...
                              Ответить
                              • Буфер под мелкие строки.
                                Ответить
                              • Предполагаю, что шланг показал размер не самой структуры, а размер указателя на эту структуру (компилировал в 32-битном режиме).
                                Ответить
                                • Если в шланге конструктор std::string не возвращает указатель на созданный объект (а это слегка бредово), то sizeof должен вернуть размер именно std::string.
                                  Ответить
                                  • В кислородном или масляном?
                                    Ответить
                                    • В водяном.
                                      Ответить
                                      • Я - водяной, я - водяной,
                                        Поговорил бы кто со мной,
                                        А то мои подружки - пиявки,да лягушки!
                                        Фу, какая гадость!
                                        Эх, жизнь моя - жестянка!
                                        Да ну ее в болото!
                                        Живу я как поганка,
                                        А мне летааааать, а мне летааааать, а мне летать охота!
                                        Ответить
                                      • А ты не сравнивал инспекции clang с пивасой?
                                        Ответить
    • Переведи на "Scratch 1.4".
      Ответить

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