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

    +50.7

    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
    namespace inter {
        template<typename X>
        struct tplClass {
            private:
                X _priv;
            
            public:
                tplClass(const X _generator) {
                    this->_priv = _generator;
                    return;
                }
                
                tplClass(const tplClass<int>& _a) {
                    this->_priv = _a.get();
                    return;
                }
    
                const X get(void) const {
                    return this->_priv;
                }
        };
        
        
        template<typename X>        
        const tplClass<X> operator+(const tplClass<X>& _lha, const tplClass<X>& _rha) {
            return tplClass<X>(_lha.get()+_rha.get());
        }
    }
    
    int main(int argc, char* args[], char* envs[]) {
        tplClass<int> a(5);
        tplClass<double> b(3.1);
        tplClass<double> c = a + b;
        return 0;
    }

    1. Всё упрощено до невозможности. Описано только то, что необходимо для узрения говнокода.
    2. Несмотря на то, что решение существует в очень известной книжке, я напоролся на говнокод лично, пока программу писал. Искал несколько дней проблему... Ну да... Бывает...
    3. Говнокод заключается в том, что данный код не компилируется.

    P.S. Если видите, что где-то есть дыра, то говорите, возможно я опечатался (злобная администрация исправить не даст), возможно в данном коде плюг опущен, возможно дыра у меня в голове. Конструктивная критика приветствуется.

    P.P.S. Если вы решили просто написать, что (C++ == "говно") is true , то, пожалуйста, прошу вас как людей порядочных и воспитанных, имеющих совесть и ум, пройдите мимо. Если у вас нит ни ума, ни совести, ни порядочности, то можете писать про "естественную говнистость" C++, милости прошу.

    Запостил: interested, 30 Октября 2009

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

    • Ну вот... Так и знал... =[
      Понятно, что на самом деле всё происходит в моём пространстве имён...
      Ответить
    • [quote]
      const X get(void) const {
      [/quote]
      Такие вещи же по ссылке возвращаются.
      Ответить
      • Какой смысл возвращать ссылку на закрытый объект? А если я потом константность сниму кастом, что будет? Я неявно предоставлю доступ к закрытым данным, что не есть хорошо.
        Ответить
        • Наверно, смотря какой степени инкапсуляции вы хотите добиться и какими данными будите оперировать. И вероятнее всего, для стандартных типов (как в Вашем примере int, double и т.д.) необходимости в данном врапере нет. А, например, для громоздких объектов, работающих с большими объемами данных, возвращение копированием будет накладно (особенно при реализации своего перегруженного сложения, для которого спокойно можно использовать возвращение результата по ссылке)
          Ответить
        • Если константность снимать - что угодно может случиться, это не аргумент.
          Ответить
          • Это к тому про "снимать константность", что таким образом легче увидеть, что данные на самом деле уже и не закрытые получились. Хотя в той же книжке Мейерса написано, что константность всё решает. Однако, там же описано, что могут появляться "висячие дескрипторы"...
            Правильнее сказать, что я не сторонник подобных мер.
            Benin пишет правильно, что многое зависит от ситуации. Например, если мне пришлось возвращать "тяжёлый тип".
            Мне такое делать приходилось. Но после недолгого "перепроектирования" получилось добавить сущность, которая что-то в себя инкапсулировала и агрегировалась другими классами.
            Ответить
        • Использовать const_cast - зло. Только если дебажить или чёта проверить. В готовом коде его быть не должно.

          Если по делу, то это учебный недоговнокод.
          Ответить
          • Я столкнулся с этим, когда писал код для своих целей.
            Никогда раньше не сталкивался с такой особенностью неявного привидения типов.
            Ответить
    • А в чем собственно вопрос?
      ну да operator+ тут не сможет инстанциироваться из-за неоднозначности (мб int, мб double)
      Ответить
      • Не в неоднозначности дело.
        Компилятор вообще говорит, что не найден оператор сложения.
        Если написать два честных, нешаблонных, сложения, то код скомпилируется и верно выполнится
        Ответить
        • template<typename X, typename Y>
          const tplClass<X> operator+(const tplClass<X>& _lha, const tplClass<Y>& _rha) {
          return tplClass<X>(_lha.get()+_rha.get());
          }
          Ответить
          • В таком шаблоне тип результата будет зависеть, от типа первого параметра. tplClass<int> + tplClass<double> != tplClass<double> + tplClass<int>
            Ответить
            • это был quick fix :)
              естественно, зависит от первого параметра.
              можешь специализировать шаблон, но вообще, это ахтунг - делать такие операторы.
              Ответить
              • Да вот взбрело мне в голову написать специальные свои матрицы. И сделать их шаблонами. Я вообще с шаблонами не так давно начал программировать, где-то год назад я первую прогу с ними вывел. И вот у int матриц появилась неприятность их обращения. Они при обращении становятся часто нецелыми. Я написал кастинг по Страуструпу, написал inv функцию обращения и передавал туда итовые матрицы. Потом я решил складывать матрицы, и тут был он, ахтунг...
                Матрицы не складываются...
                Если по-вашему писать (я пробовал), то выходит фиговрина =[
                С тремя параметрами та, же фиговина... Но там я чувствовал, что есть двусмысленность...
                И пошёл искать траблу...
                Вот нашёл через некоторое время. Я сначала думал посмотрю в библиотеке стандартной, там же есть complex<typename _Tp>, да так и не разобрался, очень что-то замудрёно всё...
                Ответить
              • про специализацию ляпнул не подумавши, нужен третий параметр, которого оператор получить не может.
                думаю, лучше не парить мозг а написать шаблонный Add.
                что-то вроде
                template<typename R, typename X, typename Y>
                const tplClass<R> Add(const tplClass<X> x, const tplClass<Y> y)
                {
                return tplClass<R>(x.get() + y.get());
                }
                Ответить
                • Ну ...
                  Мне вот очень хотелось именно такую вычурню сделать =] Вот прям жёсткое желание себе в попу вставить, да поглубже =] И ведь нашёл у Скотта Мейерса как раз такую штуку! =]
                  Ответить
        • Ну вот и не видит из-за неоднозначиности!
          Ответить
          • Когда я писал этот код, то неоднозначности не увидел.
            Глядим.
            Можно сварганить две функции: int int и double double ; По сути ни одна из них не может удовлетворить поставленной перед компилятором задачи. Он и не находит функцию. Вот в чём дело!
            Однако, если функции будут не шаблонные, а ручками прописанные, то код сработает.
            То есть, при выводе шаблона не работает кастинг типов!!!
            Ответить
            • Естественно не работает. Он же не может перебрать все варианты и выбрать подходящий. Тогда это будет уже не язык, а один большой побочный эффект.
              Ответить
              • Для меня это оказалось не естественным... Ибо я честно написал преобразование из int во что-то. И казалось, что стоит только ему заглянуть, что МОЖНО из int сделать double, как всё заработает.
                Ан нифига...
                Ответить
                • В таких случаях лучше не перегружать оператор, а делать что-то вроде
                  c = a.Add<double>( b ); чтобы можно было явно указать тип параметра.
                  Ответить
                  • и желательно тип возвращаемого значения, а то получится то, что я выше написал.
                    Ответить
            • Небольшая поправка: не int int и double double, а tplClass<int> tplClass<int> и tplClass<double> tplClass<double>

              Всегда Ваш, К.О.
              Ответить
    • return внутри конструктора — это сильно! :)
      Ответить
      • И чего? Я использую иногда, чтобы выйти из конструктора. Например выполнилось какое условие, я выхожу...
        Но в конструкторе здесь есть некорректность.
        Ответить
        • Да нет, ничего… Вам бы Макконнелла почитать ;)
          Ответить
          • Макконеллы, вероятно, бывают разные. С одной фамилией я знаком по книге "Совершенный код". Но... Весьма приблизительно... Вообще не встречал замечаний относительно "выполнения конструктора до конца".

            Если вам известны проблемы, то вы прямо и напишите: так вот и так, возникают неприятности. Я перестану писать его там...
            Ответить
            • Назначение конструктора — инициализировать объект. Посему, выполнение метода, инициализирующего объект, *не до конца* как-то не вяжется со здравым смыслом. А значит, это источник потенциальных ошибок в будущем.
              Ответить
              • Вообще, если получается, что объект создан не до конца, значит при создании возникла неприятность такого характера, что создание объекта для программы не имеет смысла.

                Например я пытаюсь на очердной сетке, создать новый объект обсчёта, от этой сетки. Однако, метод обсчёта требует выполнения некоторых критериев устойчивости. Я проверяю эти критерии для переданной сетки значений начиная с самого простого. Если какой-то из критериев не выполняется, я логирую проблему и выхожу из конструктора. Создающая обсчёт программа или клиентский объект, увидев, что произошла ошибка удалит счётный объект и попытается прибегнуть к другой схеме или вовсе прекратит работу, сообщив, что всё уже вне пределов действия модели.

                Простой пример, метод прогонки, который хочет, чтобы для переданной матрицы граничные прогоночные коэффициенты были бы меньше одного, а сумма побочных диагоналей меньше основной. Проверить начальные коэффициенты очень легко, дело трёх операций, а вот подсчёт суммы -- операция трудоёмкая. Если коэффициенты уже неверны, продолжать проверку и обсчёт нет смысла, нужно обращать матрицу приближённо, итерациями.
                Ответить
                • В таких случаях используеться throw (генерация исключения).
                  Тебе даже не придеться делать удаление обьекта, тк компилятор сделает это за тебя.
                  Если за тебя что-то делает компилятор, то ты уверен в этом, тк он точно ничего не забудет, в отличии от тебя. А на практике часто где-нибудь да забывают...
                  Ответить
                  • Если в конструкторе ты выделил для чего-то память не на стеке, а затем выпадет исключение, то ничего компилятор удалять не будет сам. Всё выделенное через new канет в Лету.
                    Ответить
                  • Это вы погорячились.
                    Генерация исключения в конструкторе может приводить к непредвиденному результату.
                    Ответить
                    • Не такому уж и нипридвиденому. А вот в деструкторе - да.
                      Ответить
                      • Ну,да. Не то, чтобы к неопределённой ситуации, но результат зависит от вашего класса, от его поставщиков, как они ведут себя при конструировании. Даже если некоторый класс не создаёт утечки памяти сейчас, то когда он станет элементом системы, то при инициализации в конструкторе клиента, последний может выкинуть исключение, когда уже сформированы элементы динамической памяти. Иметь конструктор с исключением, как ходить по минному полю: вроде и радар есть, а всё же ногу оторвать может...
                        Ответить
              • Вообще, я не пытаюсь двигать return из конструктора в массы, уж упаси Эслан.

                И такие вот у меня конструкторы встречаются someClass(unsigned int size):_array(valarray<double>(0.0,size)) { }
                Ответить
                • Если return в конструкторе, то ничего страшного, просто можешь забыть что-то инициализировать, если напишешь это после return.
                  А вообще он просто не нужен в тех функциях, в которых ты не возращаешь параметры. Незнаю зачем писать несколько лишних букв.
                  Ответить
                  • Да. Зачастую это просто несколько лишних символов и я их не пишу. Однако, иногда бывают случаи, когда функция может завершиться в различных точках, чем нагромождать каких-то сторонних хитрых конструкций лучше, по-моему, лишний пустой return воткнуть. И каждый будет видеть, что вот тут она уже завершилась.

                    Говнокод с return сейчас продемонстрирую.
                    Ответить
                    • Пустой return, если следом идёт выход из функции - это и есть нагромождение лишних конструкций.
                      Ответить
    • Мой знакомый друг-индус всегда пишет не менее 19 return в конце каждой функции. Это даёт ему приличную прибавку к зарплате.
      Естественно несколько return'ов - это для пущей надёжности. Хоть один, да сработает. Поэтому он не краснея смотрит начальнику в лицо и говорит, что пишет самый надёжный код на планете.
      Ответить
    • добавлю свои 5 копеек

      1) есть неприятность с конструктором:

      tplClass(const tplClass<int>& _a)

      зачем привязка только к int?

      2) в принципе, можно было бы сделать, чтобы int+double и double+int возвращали double, если бы был typeof. Скажем, так:

      template <typename T1, typename T2>
      struct get_sum_type
      {
          typedef typeof(T1()+T2()) type;
      };
      
      template <typename T1, typename T2>
      get_sum_type<T1,T2>::type operator+(const tplClass<X>& _lha, const tplClass<X>& _rha)
      ...


      может быть в boost::mpl есть что-нибудь заместо typeof. Некоторые компиляторы предоставляют что-то подобное. Но, опять же, хотелось бы обходиться без обязательного наличия конструкторов по-умолчанию.

      Можно попробовать, например, через boost::mpl описать правила привидения типов, в соответствии со стандартом. Например,

      template <typename T1, typename T2>
      struct get_common_type
      {
          typedef
              mpl::if_<
                  mpl::and<
                      mpl::is_integral<T1>
                      , mpl::not<mpl::is_integral<T2> >
                  >
                  , T1
                  , mpl::if_<
                  mpl::and<
                      mpl::is_integral<T2>
                      , mpl::not<mpl::is_integral<T1> >
                  >
                  , T2
                  , ...... и т.д.
      };


      , но, наверное, это чересчур геморно :)
      Ответить
      • не заметил ещё одно:

        не надо делать имена с подчёркиванием в начале, ни одним, ни двумя, - они зарезервированы для компиляторов и писателей стандартных библиотек.
        Ответить
        • Мета программирование -- это, разумеется, очень здорово. Мне с ним не приходилось работать. Надо бы погрузиться. Но здесь задача гораздо проще. =]
          Мне приведение типов было нужно всё равно, для обращения и для других функций, думалось, что всё с шаблонным сложением сработает, а оно не захотело. Я запостил. =]

          >зачем привязка только к int?
          Этот неявный конструктор и есть неявное преобразование из tplClass<int> в любой другой. Предполагается, что их тут два tplClass<int> и tplClass<double>.
          Предлагаете другой способ неявного привидения типов?

          Вообще было бы правильным решением сделать код, который бы генерировал код для неявного преобразования шаблонных классов для используемых в программе конкретизаций, типы которых неявно приводимы. (и что было бы тогда с tplClass< tplClass<int> >? Как вычислить все типы к которым он приводится...)

          >не надо делать имена с подчёркиванием в начале
          Буду иметь в виду.
          Я не пишу обычно подчёркиваний. яПишутВотТакВотССамогоДетства. Просто я код "передрал" и для однообразия всем добавил _ =] По-моему из STL для какого-то сложения был код, я его выдрал и перекроил, чтобы сюда поднять.
          Я же сначала в STL и полез поискать решение проблемы, а не в книжки... Думаю, разберусь в коде, сделаю, как они. А там нету... =[
          Ответить

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