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

    +46

    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
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    //Сом ненужный щит
    
    #include <iostream>
    using namespace std;
    
    #include <string>
    #include <iostream>
    
    struct Tracer {
       Tracer(void)
          :m_name("(none)")
       {
          std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
       }
       Tracer(const std::string & name)
          :m_name(name)
       {
          std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
       }
       Tracer(const Tracer & other)
          :m_name(other.m_name)
       {
          std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
       }
       Tracer(const Tracer && other)
          :m_name(other.m_name)
       {
          std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
       }
       Tracer & operator=(const Tracer & other) {
          m_name = other.m_name;
          std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
          return *this;
       }
       Tracer & operator=(const Tracer && other) {
          m_name = other.m_name;
          std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
          return *this;
       }
       ~Tracer() {
          std::cout << "[" << m_name << "]    " << __PRETTY_FUNCTION__ << std::endl;
          m_name="You looser!";
       }
    
       std::string m_name;
    };
    
    //Тот щит, ради чего всё затевалось
    
    template<class T> const T&  Min(const T &x, const T &y) { return (x.m_name < y.m_name) ? x : y; }
    
    int main() {
      const Tracer& mr = Min(Tracer("a"), Tracer("b"));
      cout<<"Some work with mr: "<<mr.m_name<<endl;
      return 0;
    }

    [b] Tracer::Tracer(const string&)
    [a] Tracer::Tracer(const string&)
    [a] Tracer::~Tracer()
    [b] Tracer::~Tracer()
    Some work with mr:

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

    Запостил: LispGovno, 04 Февраля 2014

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

    • Как я облажался...
      http://ideone.com/4J873s
      Ответить
      • Кстати трабла тут даже не в самом Min. Он то отработал абсолютно корректно, прокинув ссылку насквозь. Об этом надо просто не забывать.

        А ошибка, имхо, именно в сохранении ссылки на временный объект (один из трейсеров) в строке 53...
        const Tracer& mr = Tracer("a");
        Интересно, почему в этом коде временный объект умирает после вывода, а не до?
        Ответить
        • Я всегда считал const Tracer& mr = Tracer("a"); UB'ом... Это не так?
          Ответить
          • Это не UB. Tracer живет до конца выражения и const & продлит жизнь, если написано хорошо выражение. Кто неправ - не знаю. Чувак, что пишет функции - может в легкую проанализировать и убрать ошибку. Чувак что пишет выражение - не может это сделать легко. Так что я бы переложил ответственность на писателя функций, как на человека, легко не делающего ошибки, если следует простому правилу: не вертай ссылки.
            Избавится от const Tracer& не всегда возможно. Может быть важно для оптимизации. Юзают в паттерне RAII типа scope_exit. Иногда ссылка нужна для полиморфизма. Иногда нужна для того чтобы не было копирования. Да просто ответственность по возвращаемым ссылкам нужно возлагать на писателя функций, чтобы не плодить правила (правило: не возвращай ссылки на временные объекты - лучше из этого правила просто убрать последние слова и переписать как не возвращай ссылки вообще). В конце концов когда в функции вдруг появятся редкие условия с временными объектами, то ты вдруг как оказалось нарушил и ещё одно правило с возвращаемыми ссылками. Я тоже вспомнил, что я лажал именно так же. Банально соптимизировал. А потом через полгода исправил ошибку добавив редко исполняемое условия с временным объектом. Сейчас я понял что тогда исправив ошибку - я добавил новую. И эта ошибка не исправлена до сих пор в коде где-то.
            Ответить
            • > const & продлит жизнь
              А где можно об этом почитать?

              Ну а про невозврат ссылок согласен. Правда иногда надо, например скобки у контейнеров.
              Ответить
              • Дефейкейстра докладывает. Его спрашивай. Емнип в стандарте.
                & - не продливает.
                const & продливает на один уровень. Ну то есть если ты напишешь перекачивание ссылки: из const A & в const A &, а из неё в const A& или в const B&, то это фейл. В конце жизни второй ссылки все пойдет в помойку и в третей ссылке уже может оказаться говно. Хотя конечно не обязано и компиляторы в некоторых случаях по ошибке или по стандарту могут сохранять жизнь и поэтому код может показывать неплохие жизненные показатели от компилятора к компилятору или от ключей компиляции к ключам компиляции.
                Также ссылка до конца выражения доживет, например выражение:
                f(m(), g(c, t(), d),m()); функция или конструктор g второй параметр принимает по константной ссылке, то до конца вызова g t() будет жить. После начала кода вызова f - t() уже если я правильно помню помрет. То есть сформированный во время вызова g временный объект (будь то объект типа g в случае конструктора или объект другого типа, если g функция) уже не будет иметь доступа к живому результату работы t(). Тот уже откинет копыта. Все что выше говорил - относится лишь к константным ссылкам. Не константные не продлевают, а это значит f(g()) - или UB или ошибка компиляции, если f принимает не константную ссылку. Вроде это любили превращать в UB без ошибки компиляции студии старые.
                Ответить
              • На самом деле мне очень интересно как ведет себя продливание ссылок по отношению к
                m = {t(),t(),t()};
                (std::initializer_list и конструкторам вызванным посредством {...} и агрегатным типам). Происходит ли при этом копирование объектов из {...}. Где они располагаются? В стеке? Как скоро умирают по отношению к ссылкам на них? Как это совместимо с alloca? Если кинет исключение 3тий объект в списке инициализации, то что произойдет? Все отматается? В обратном порядке?
                Ответить
              • О боже, Борманд чего-то не знает, да неужели??
                Ответить
            • > паттерне RAII типа scope_exit
              А что за паттерн? С ведрофона гуглить лениво.
              Ответить
              • loki scopeguard гугли. возвращает функция константную ссылку на базовый класс и принимающий в макросе также константную ссылку принимает
                Ответить
                • Спасибо, завтра почитаю.
                  Ответить
                  • Почитал?
                    Ответить
                    • Почитал статью Александреску, где он придумал этот скоп гард. Но там я что-то не нашел этой странной ссылки. Надо прям в реализации копаться?
                      Ответить
                      • Статью? Я в реализации копался. Там была. Только почему странная?
                        Ответить
                        • http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758
                          Ответить
                        • > Только почему странная?
                          Потому что функция возвращает ссылку ;) По-моему этого факта достаточно, чтобы, по крайней мере, внимательно относиться к такой функции.
                          Ответить
                          • UPD: Почитал исходник, там MakeGuard возвращает не ссылку, а самый обычный временный объект. Который потом помещается в const ScopeGuardImplBase& и выживает до конца скопа за счет описанного тобой выше правила.
                            Ответить
                          • Я перепутал. Там функция возвращает по значению Derived. Результат функции в макросе принимается в const Base&. Притом у Base нет виртуального деструктора, но ~Derived всё равно сам вызывается и это согласуется со стандартом.
                            Ответить
      • показать все, что скрытоСосни хуйца, хуле.
        Ответить
    • так вроде же гцц кидает варнинг если пытаешься вернуть временный объект?

      (протестил на 4.8.2 - с -Wall -Wextra нет ворнинга.)

      как по мне: вот к чему приводит злоупотребление reference'ами. ;-)

      PS ща пофикшу. заменяешь вот это:

      template<class T> const T& Min(const T &x, const T &y) { return (x.m_name < y.m_name) ? x : y; }

      на вот это:

      #define Min( x, y ) ( (x).m_name < (y).m_name ? x : y )

      и, о чудо, оно работает!

      PPS я в таких случаях еще вывод (void *)this добавляю что бы видно было как именно объекты глючат.
      Ответить
      • Вот только таких макросов не хватало. Подумаешь, посчитает все по 2 раза ;)
        Ответить
        • :)

          тестил, создает еще одну инстанцию объекта автоматом. и печатает корректно "а".
          Ответить
    • Присвоение в деструкторе незаметно доставило.
      Ответить
    • // то есть, если у нас есть такая примитивная функция:
      const T& function(const T& x) {
        return x;
      }
      // то тогда
      const T& x = A(); // <- это норм
      const T& y = function(A()); // <- это UB

      Я правильно понял?
      Ответить
    • узбекски минусовка
      Ответить

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