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

    +12

    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
    #include <iostream>
    #include <functional>
     
    using namespace std;
     
    struct  T
    {
        int a;
        T(const T&):a(1){cout<<"copy_construct T"<<endl;}
        T():a(1){cout<<"construct T"<<endl;}
        ~T(){a=0;cout<<"destruct T"<<endl;}
        const T& operator=(const T&){cout<<"copy_ass T"<<endl;return *this;}    
    };
     
    struct M: public T{};
     
    void f(const T& fa)
    {
        cout<<"fa.a= "<<fa.a<<endl;    
    }
     
    int main() {
        f(std::cref(T()));
        cout<<endl;
        const T& b = T();
        cout<<"b.a= "<<b.a<<endl;
        cout<<endl;
        const T& a = std::cref(T());
        cout<<"a.a= "<<a.a<<endl;
        return 0;
    }

    http://ideone.com/BmHo9w
    Есть на этом ресурсе великий знаватель крестов и вот он меня уверял, что объект, на который ссылается ссылка - должен дожить до конца выхода ссылки из скоупа. Почему мы этого не наблюдаем? А знаватель? Ты меня прямо даже убедил, и тут такая подстава от тебя. a - не дожил до конца.

    Запостил: LispGovno, 03 Декабря 2012

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

    • Мне здесь повезло в fa и b или по стандарту на этот счет есть какие-нибудь гарантии?
      Ответить
      • всё как и задумано
        reference_wrapper нельзя использовать для temporary objects
        именно потому, что они умрут при выходе из конструктора (что аналогично случаю с f)
        ну а b дожил до конца, как и следовало
        Ответить
        • То есть с f тоже лишь повезло и по стандарту T должно было умереть до входа в функцию?
          Ответить
          • почему до входа?
            при выходе из функции
            Ответить
            • То есть конструкция f(std::cref(T())); по стандарту всеже валидна?
              Ответить
              • я это упустил
                глаза изначально увидели f(T()), что, понятное дело, валидно
                думаю, что f(std::cref(T())) - примерно тот же случай, что и const T& a = std::cref(T());
                т.е. должно быть плохо

                з.ы. ну и gcc-4.7 со мной согласен
                : error: use of deleted function ‘void std::cref(const _Tp&&) [with _Tp = T]’
                Ответить
                • з.ы. ну и gcc-4.7 со мной согласен
                  : error: use of deleted function ‘void std::cref(const _Tp&&) [with _Tp = T]’


                  Это радует. Очень хорошо. Но в стандарте я такой перегрузки не видел (им бы поправить его).
                  Даже здесь её нет:
                  http://en.cppreference.com/w/cpp/utility/functional/ref

                  Кстати, вот себя delete наконец положительно проявил.
                  Ответить
                  • в pdf есть
                    Ответить
                  • вот же, все на месте
                    http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper/reference_wrapper
                    Ответить
                    • gcc:
                      >error: use of deleted function ‘void std::cref
                      стандарт:
                      >reference_wrapper( T&& x ) = delete;

                      Что-то они странно подошли к пониманию стандарта.

                      стандарт говорит, что удаляет конструктор перемещения, когда гцц говорит, что удалена функция цреф от рвалуессылки.
                      Ответить
                      • имхо гцц тут больше прав. иногда конструктор пермещения нужен для reference_wrapper, а вот для цреф рвалуе действительно создает траблы и правильно, что в гцц удалили
                        Ответить
        • А так хоть можно?
          f((T()));

          http://ideone.com/J1LvwT
          Ответить
        • А вот если там какое-то арифметическое выражение с каким-нибудь intоподобным интерфесом и операторами. Если там используются какие-то запоминания временных объектов по ссылкам, например для будущего довычисления (ленивые вычисления) или ещё какого полезного возможнохака. И запись:
          f(a*5+r); Получается по стандарту до конца функции f должен выжить только объект t в следующем псевдокоде после раскрытия?
          auto t = a*5+r;
          f(t);
          а остальные ссылки сохраненные внутри объектов окажутся дохлыми?
          Ответить
          • да
            это всё есть в [class.temporary]
            Ответить
            • Тогда ты что-то недоговариваешь или путаешься в показаниях в случае с f(std::cref(T())):
              В (std::cref(T())) - cref такая же функция, как operator* в (T()*5)
              Это значит, что в обоих случаях T() должно умереть (а во втором случае ещё и объект сконструированный из 5ки, если он был) во время передачи результата следующему надвыражению, например в функцию f.

              зы:
              Я всегда буду обновлять тред перед постингом.
              Я всегда буду обновлять тред перед постингом.
              Я всегда буду обновлять тред перед постингом.
              Я всегда буду обновлять тред перед постингом.
              Ответили выше
              Ответить
              • да, я же написал
                глянул мельком код в ОП
                глянул выхлоп
                суть, что там cref в параметрах f, не увидел, поэтому ничего не заподозрил - ну и дальше рассуждал, что там просто f(T())

                > Я всегда буду обновлять тред перед постингом.
                только это не удобно нихера
                Ответить
                • Хорошо, новая ситуация:
                  Мне казалось, что раньше я в ней был уверен, но теперь совершенно не уверен:
                  const T& f(const T& a)
                  {
                    return a;

                  Имею ли я в данном случае передать а по ссылке или по выходу из функции а может по стандарту издохнуть и ссылка будет указывать на мусор?
                  Ответить
                  • Ну поведение gcc ожидаемое, ничего не поменялось:
                    http://ideone.com/3hOQ63

                    #include <iostream>
                    #include <functional>
                     
                    using namespace std;
                     
                    struct  T
                    {
                        int a;
                        T(const T&& b):a(b.a){cout<<"move_construct T"<<endl;}
                        T(const T& b):a(b.a){cout<<"copy_construct T"<<endl;}
                        T():a(1){cout<<"construct T"<<endl;}
                        ~T(){a=0;cout<<"destruct T"<<endl;}
                        const T& operator=(const T&b){a=b.a;cout<<"copy_ass T"<<endl;return *this;}    
                        const T& operator=(const T&&b){a=b.a;cout<<"move_ass T"<<endl;return *this;}    
                    };
                     
                    struct M: public T{};
                     
                    const T& f(const T& fa)
                    {
                        cout<<"fa.a= "<<fa.a<<endl;
                        return fa;
                    }
                     
                    int main() {
                        const T& c=f(std::cref(T()));
                        cout<<"c.a= "<<c.a<<endl;
                        cout<<endl;
                        const T& d=f(T());
                        cout<<"d.a= "<<d.a<<endl;
                        cout<<endl;
                        const T e=f(T());
                        cout<<"e.a= "<<e.a<<endl;
                        cout<<endl;
                        const T& b = T();
                        cout<<"b.a= "<<b.a<<endl;
                        cout<<endl;
                        const T& a = std::cref(T());
                        cout<<"a.a= "<<a.a<<endl;
                        cout<<"work"<<endl;
                        return 0;
                    }


                    Но что на эту тему говорит стандарт?
                    Ответить
                    • http://ideone.com/bmton0

                      #include <iostream>
                      #include <functional>
                       
                      using namespace std;
                       
                      struct  T
                      {
                          int a;
                          T(const T&& b):a(b.a){cout<<"move_construct T"<<endl;}
                          T(const T& b):a(b.a){cout<<"copy_construct T"<<endl;}
                          T():a(1){cout<<"construct T"<<endl;}
                          ~T(){a=0;cout<<"destruct T"<<endl;}
                          const T& operator=(const T&){cout<<"copy_ass T"<<endl;return *this;}    
                          const T& operator=(const T&&){cout<<"move_ass T"<<endl;return *this;}    
                      };
                       
                      const T& f(const T& fa)
                      {
                              return fa;
                      }
                       
                      const T g(const T& fa)
                      {
                              return fa;
                      }
                       
                      int main() {
                          const T& d=f(T());
                          cout<<"d.a= "<<d.a<<endl;
                          cout<<endl;
                          const T& c=g(T());
                          cout<<"c.a= "<<c.a<<endl;
                          cout<<endl;
                          cout<<"work"<<endl;
                          return 0;
                      }


                      Вобщем ответ на мой вопрос:
                      Печально известный способ оптимизации функций пробрасывания ссылки сквозь ссылку:
                      const T& f(const T& a)
                      {
                          return a;
                      }
                      - может приводить к UB. Заглядывание в стандарт не понадобилось.
                      Ответить
              • > Я всегда буду обновлять тред перед постингом.

                Стырил бормандовскую фишку, гад.
                Ответить
                • окей.жпг:
                  Я всегда буду обновлять тред перед постингом.(с)
                  Ответить
                  • Отдаю эту фразу в общественное достояние.

                    Лишь бы ее не уродовали псевдознаком копирайта, составленным из скобок и буквы с.
                    Ответить
    • А я думал что Тарас - знаватель креста.
      Ответить
    • struct M: public Monad
      Ответить
    • Кто знает, куда пропал
      http://liveworkspace.org/
      (онлайн компилятор крестов самый свежий и буст)
      Может куда переехал?
      Ответить
      • чувак делает ливворкспейс 2.0
        но он вообще давно просрал все обещанные сроки
        Ответить
        • Где почитать и где следить за прогрессом?
          Ответить
          • хз
            http://www.gamedev.ru/flame/forum/?id=169251
            Ответить
            • лучший ресурс был. где теперь вырвиглазое сверхсовременное говно мне тестировать?
              Ответить
              • ideone? Или обязательно нужен буст?
                Ответить
                • Ну если знаешь без буста самый современный компилятор онлайн, то советуй. Если знаешь с бустом не самый современный, то тоже.

                  >ideone? Или обязательно нужен буст?
                  Ты не поверишь:
                  http://ideone.com/JsvfGK
                  Ответить
                  • Ух. Так чем ideone плох? Тем, что только один файл можно впихнуть?
                    Ответить
                  • > советуй
                    http://codepad.org/ <- слишком простой
                    https://compilr.com/ <- нормальный
                    http://krillapps.com/coderunner/ <- хороший
                    Ответить
        • >чувак делает ливворкспейс 2.0
          (покидать рабочее пространство)
          Если честно, не нужен. Лайвворкспейс 1.0 был прост как пробка и всегда самый новый. За это он мне и нравился.
          Если они введут мудню с регистрацией, управлением файловой системой и проектами, то это будет не приятно. Придется с него уйти.
          Ответить
          • > Придется с него уйти.
            верно
            ни строчки врагу!
            Ответить
            • Кстати, вчера зарегался на ideone, предложили поставить пароль для какого-то ideone api. Не в курсе, что там за api доступно? Доки по нему не нашел, наверное плохо ищу.
              Ответить
              • без понятия
                у меня даже логина туда нет
                Ответить
              • Я всегда буду смотреть в подвал страницы: http://ideone.com/api
                А на некоторых сайтах типа ВК просмотр подвала стал задачей нетривиальной...
                Ответить
    • Посоны, смотрите к чему нас готовят:
      В кресты перенесут Loki::TypeList
      http://gcc.gnu.org/onlinedocs/gcc-4.7.2/libstdc++/api/a00905.html
      А ещё начинают готовить рефлекшен:
      Получение списка всех базовых классов класса во время кококомпиляции (интересно, как это реализовано, наверное вшили в компиль):
      http://gcc.gnu.org/onlinedocs/gcc-4.7.2/libstdc++/api/a00907.html

      зы:Так и есть, вшили, скукота:
      template<typename _Tp>
                struct bases
                {
                  typedef __reflection_typelist<__bases(_Tp)...>        type;
                };

      http://gcc.gnu.org/onlinedocs/gcc-4.7.2/libstdc++/api/a01526_source.html
      Ответить
    • Для чего нужна struct M?
      Ответить
    • Вот я дописал функцию
      T& f(const T& fa)
      {
      static int i=0;
      cout<<"fa.a= "<<fa.a<<" i="<<i<<endl;
      while(i++<=10){
      f((T&)fa);
      }
      return (T&)fa;
      }
      Ответить
      • http://ideone.com/eJDor1
        ты что-то этим кодом хотел про демон монстрировать?
        Ответить
        • hans@hans-134:~/Downloads$ gcc -o p1 ideone_eJDor1.cpp
          ideone_eJDor1.cpp:9:14: error: expected �,’ or �...’ before �&&’ token
          ideone_eJDor1.cpp:9:18: error: invalid constructor; you probably meant �T (const T&)’
          ideone_eJDor1.cpp:14:31: error: expected �,’ or �...’ before �&&’ token
          Ответить
          • Название языка не перепутали? Use gcc -std=c++0x, Luke!
            Ответить
            • С g++ тоже самое.
              Ответить
              • Разобрался, что ты имел в виду:
                $ g++ 1.cpp
                1.cpp:9:14: ошибка: expected «,» or «...» before «&&» token
                1.cpp:9:18: ошибка: некорректный конструктор; возможно, вы имели в виду «T (const T&)»
                1.cpp:14:31: ошибка: expected «,» or «...» before «&&» token
                Решается эта проблема добавлением опции -std=c++0x, т.к. код юзает фишки нового крестостандарта.

                P.S. Если нормально скопировать сообщения не получается, сделай export LC_MESSAGES=C перед стартом gcc, чтобы он писал по-английски.
                Ответить
            • gcc вполне компилит плюсики, ведь это всего лишь фронтенд, который по расширению вызывает нужный бекенд. Но вот если его вызвать не как g++, а как gcc он не подлинкует стандартную крестолибу. Собственно если добавить -lstdc++, то все соберет без проблем.

              P.S. Что-за мусор в непонятной кодировке у AliceGoth я не понял...
              Ответить
              • Придётся вспомнить добрым словом Watcom C, у которого была #pragma, добавляющая в объектный файл комментарии, которые линкер воспринимал как инструкцию по подключению библиотек и других объектных файлов... Жаль, что это не стало стандартом.

                Кстати, как у других компиляторов с указанием параметров сборки в исходнике?
                Ответить
                • у студии - #pragma comment(lib, "...")
                  у гцц такого нет, что печально
                  у clang, судя по всему, есть
                  Ответить
                  • > что печально
                    Да было бы о чем печалиться... имхо это очень сомнительная фича.

                    Опции будут рассеяны по нескольким местам - часть в сборочной системе, часть в исходнике. До кучи кто-нибудь додумается добавить эту прагму в хидер (удобно же чтобы либа цеплялась сразу, когда заинклудили относящийся к ней хидер).

                    В результате вместо одного взгляда на командную строку компилятора/компоновщика придется потратить кучу усилий на копание в коде и всех его инклудах...
                    Ответить
                    • #define BOOST_LIB_DIAGNOSTIC

                      Компиляция...
                      outer.cpp
                      Linking to lib file: boost_thread-vc90-mt-1_49.lib
                      Linking to lib file: boost_date_time-vc90-mt-1_49.lib
                      Linking to lib file: boost_system-vc90-mt-1_49.lib
                      Linking to lib file: boost_regex-vc90-mt-1_49.lib
                      Linking to lib file: boost_filesystem-vc90-mt-1_49.lib
                      Linking to lib file: boost_chrono-vc90-mt-1_49.lib
                      Linking to lib file: boost_program_options-vc90-mt-1_49.lib
                      Linking to lib file: boost_iostreams-vc90-mt-1_49.lib
                      Linking to lib file: boost_zlib-vc90-mt-1_49.lib
                      ...

                      тяжело для каждого проекта индивидуально настраивать весь этот список

                      и ведь дело не в том, что именно тебе ненужно, дело в том что другим нужно, а гцц не умеет
                      с другой стороны, между компилятором и линкером должна быть налажена связь - каким образом передать собранный компилятором список библиотек для линковки, и, скорее всего, это и есть настоящая причина, почему гцц не умеет
                      Ответить
                      • > скорее всего, это и есть настоящая причина, почему гцц не умеет
                        Да, походу причина именно в этом. Компилятор и линкер того же watcom'а и msvc рассчитаны на работу с тулзами из своего комплекта, и скорее всего не смогут съесть список либ, записанный в объектник, подготовленный чужим компилером.

                        В gcc же, видимо, не захотели привязываться к своему линкеру (хотя для link-time оптимизации, емнип, это все равно приходится делать), а чтобы при переключении на другой линкер (тот же ld) не возникало неведомых багов, пришлось отказаться от передачи инфы для него через объектники.

                        P.S. Кстати еще одна причина, наверное, заключается в привередливости гнутого линкера к порядку, в котором перечисляются библиотеки.
                        Ответить
                        • Ну пох на опции компиляции. Просто бы запили автоподключение либ через прагму в коде. А это никаких бы проблем не натворило. Просто в чужом линкере пришлось бы прописывать список либ вручную
                          Ответить
                • > Кстати, как у других компиляторов с указанием параметров сборки в исходнике?
                  Не нужно.

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

                  Вторая проблема - далеко не все опции компилятора относятся к самому исходнику. Большая часть относится к текущей сборочной конфигурации, которых как правило несколько (например debug и release).

                  Поэтому, имхо, такая инструкция была бы полезной разве что для лаб и прочей игрушечной фигни из одного файла.
                  Ответить
                  • а меня, например, напрягает, что под виндой все нужные бустовые либы (статики или экспортные) цепляются автоматом (достаточно подключить #include <boost/some-include.hpp> и он самостоятельно разберется, какую либу потребовать), а под линуксом приходится вести вручную список, от чего же зависит мой бинарник (и получается какое нибудь говно типа boost_system boost_thread boost_filesystem boost_date_time boost_program_options boost_chrono boost_iostreams cap ssl, которое нужно актуализировать при добавлении новых бустовых инклюдов)

                    а для debug/release обычно хватает настройки выходной директории солюшена
                    Ответить
                    • Согласен, но буст это скорее исключение, нежели правило.

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

                      Если же прицеплять типичную мелкую либу, собранную в отдельном каталоге - там профита от авто-линковки практически не будет.
                      Ответить
                • >Watcom
                  Крестокомпилятор ваткома можно ещё одним добрым словом вспомнить: он поддерживает std::map на SkipList. А такой мог работать даже в многопоточной среде без локов. Насчет сравнения его производительности с RB-tree не знаю, но теже O(log n) по алгоритмической сложности.
                  Ответить
                  • у ватсона крестовость несколько условная, что до что после освобождения
                    Ответить
    • Посоны...:
      #include<iostream>
       
      struct ForTest
      {
          ForTest():mIsLive(true){}
          ~ForTest(){ mIsLive=false; }
          bool mIsLive;
      };
       
      ForTest ExampleBug() { return ForTest(); }
       
      struct MyClass
      {
          MyClass(const ForTest& src):mRef(src){}
          
          void Work()
          {
              if(mRef.mIsLive) std::cout<<" object is live\n";
              else std::cout<<" object is dead\n";
          }
          const ForTest& mRef;
      };
       
      int main()
      {
          MyClass test(ExampleBug() );
          test.Work();
          return 0;
      }

      http://ideone.com/2xrCnR
      Ответить
      • #include <iostream>
        #include <memory>
        #include <tr1/memory>
         
        using namespace std;
         
        struct VBase
        {
            virtual ~VBase(){cout<<"VBase::VBase"<<endl;}
        };
         
        struct VDerived: public VBase
        {
            ~VDerived(){cout<<"VDerived::VDerived"<<endl;}
        };
         
        struct Base
        {
            ~Base(){cout<<"Base::Base"<<endl;}
        };
         
        struct Derived: public Base
        {
            ~Derived(){cout<<"Derived::Derived"<<endl;}
        };
         
        int main() {
                {
                Derived a;
                cout<<"work a"<<endl;
            }
            {
                VDerived b;
                cout<<"work b"<<endl;
            }
            {
                tr1::shared_ptr<Base> c(new Derived);
                cout<<"work c"<<endl;
            }
            {
                tr1::shared_ptr<VBase> d(new VDerived);
                cout<<"work d"<<endl;
            }
            {
                const Base& e = Derived();
                cout<<"work e"<<endl;
            }
            {
                const VBase& f = VDerived();
                cout<<"work f"<<endl;
            }    
                return 0;
        }
        В данном конкретном куске кода ничего плохого я конечно не вижу.
        http://ideone.com/1B0Acb
        Но почему виртуальность деструктора ни на что не повлияла? Неужели соптимизировал?
        Также моя зайка Алёночка
        http://alenacpp.blogspot.ru/2008/01/const.html
        говорит, что
        //И есть еще момент с вызовом деструктора:
        
        Derived factory(); // construct a Derived object
        
        void g() {
          const Base& b = factory(); // здесь вызов Derived::Derived
          // … используем b …
        } // здесь вызывается Derived::~Derived напрямую 
        //-- а не Base::~Base + virtual dispatch!
        Ответить
        • > Неужели соптимизировал?
          нет, оптимизация тут не причем
          надо смотреть реализацию shared_ptr в гцц, потому что с голым указателем и delete работает как положено - виртуальность имеет значение
          вот тут http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr написано, что конструктор (2) использует delete expression as deleter, что может означать
          а) будет честный delete inner_ptr_; как Base (чего мы не видим), и
          б) deleter сформируется уже на этапе конструктора, и т.к. известен Y, то и delete сформируется как родной по Y * (и походу, так и есть)
          Ответить
          • То есть использование умного указателя может приводить к сокрытию ошибки в коде? Как видно, константная ссылка умудряется сделать тоже самое. Такое странное поведение ссылки по стандарту?
            Ответить
            • > странное поведение ссылки
              ничего странного
              был создан временный объект T
              должен быть удален временный объект T
              а то, что его задерживает ссылка родительского типа, компилятору не важно - имеет право, пусть задерживает
              Ответить
              • Просто забавно смотреть, как это без виртуального деструктора опознается.
                Ответить
          • Да, ты прав, указатель обычный ведет себя как и ожидается по стандарту:
            #include <iostream>
            #include <memory>
            #include <tr1/memory>
             
            using namespace std;
             
            struct VBase
            {
                virtual ~VBase(){cout<<"VBase::VBase"<<endl;}
            };
             
            struct VDerived: public VBase
            {
                ~VDerived(){cout<<"VDerived::VDerived"<<endl;}
            };
             
            struct Base
            {
                ~Base(){cout<<"Base::Base"<<endl;}
            };
             
            struct Derived: public Base
            {
                ~Derived(){cout<<"Derived::Derived"<<endl;}
            };
             
            int main() {
                    {
                    const Base* const c(new Derived);
                    cout<<"work c"<<endl;
                    delete c;
                }
                {
                    const VBase* const d(new VDerived);
                    cout<<"work d"<<endl;
                    delete d;
                }        
                    return 0;
            }

            http://ideone.com/mMioMv
            Ответить

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