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

    0

    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
    template < typename T >
    T shit (void)
    {
      return 0;
    }
    
    int main()
    {
      int crap = shit();
    // Почему дедукция аргумента шаблона в данном случае не работает?
      return crap;
    }
    
    //-------------------------------------
    
    int shit (void)
    {
      return 0;
    }
    
    // Почему functions that differ only in their return type cannot be overloaded 
    double shit (void)
    {
      return 0;
    }
    
    int main()
    {
      int crap = shit();
      return crap;
    }

    Почему плюсы такое говно?

    Запостил: j123123, 22 Января 2018

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

    • Я уже было хотел послать автора нахаскель, ведь если даже передавать переменную в шаблонную функцию как аргумент, придётся заранее иметь эту переменную, что может не сработать для константных переменных, которые нужно инициализировать здесь и сейчас, но обнаружил, что переменная Мюнхаузена вполне работает.
      template <typename T>
      T f(const T&) {
          return 9;
      }
      
      int main(void) {
          const int a(f(a));
      }

      https://ideone.com/yDMHGY
      Теперь я даже не знаю, спорить ли мне с автором или присоединяться.
      Ответить
    • почему? - пример:
      int func() { return 0; }
      float func() { return 1; }
      
      cout << bool(func()) << endl; // И чего мы тут ждем?


      Можно делать возврат прокси, кастующегося во всё что требуется. Но перегрузку по вышеописанной причине не оформишь.
      Ответить
      • bool f(int) { return true; }
        bool f(float) { return false; }
        
        std::cout << "f(true) = " << f(true) << std::endl; // И чего мы тут ждем?

        Но в обратном случае же что-то выводится в итоге https://ideone.com/SNaVKW
        И даже если в частном случае навесить типы, которые друг в друга совсем не кастуются или вызывают неоднозначность, перегрузка будет работать для некоторого набора других случаев.

        С другой стороны, с аргументом "и чего мы тут ждём?" можно много чего запретить.
        "x" * "y" // И чего мы тут ждем? Умножение не нужно!
        Ответить
        • Потому что bool -- єто такой синоним для int в крестах. Си-наследие.
          Ответить
      • int func() { return 0; }
        float func() { return 1; }
        
        std::cout << bool(int(func())) << std::endl; // можно так

        Первый каст определит то, какую из функций надо вызывать, второй уже приведет возвращаемое к нужному типу
        Ответить
        • это ровно столько же печатных знаков, что и при использовании bool(func<int>()). По факту, возможность такой перегрузки уменьшит код всего в двух контекстах:
          1. Type var = foo(); вместо auto var = foo<Type>();
          2. bar(Type t); bar(foo()); вместо bar(foo<Type>());
          Так ли это принципиально? Если да, то твои предложения в плане синтаксиса?
          Ответить
          • return foo();
            Ответить
          • > Так ли это принципиально?
            На самом деле ничего не принципиально, можно голой сишкой без всяких плюсов обходиться, накостыливая свои кодогенераторы в случае необходимости
            > Если да, то твои предложения в плане синтаксиса?
            Синтаксиса для чего? Чтоб два раза подряд что-то кастануть, никакого нового синтаксиса не нужно
            Ответить
      • В расте как-то осилили, а у крестоблядей никак. Зато факториал на этапе конпиляции можно посчитать тремя способами, хули.
        Ответить
        • Покажи, как они это сделали. Наверняка там будет месиво из impl, и тип нужно будет явно указывать. Т.е то же самое по сути, что в моём крестовом примере.
          Ответить
          • Например вот https://rustbyexample.com/types/inference.html

            Они умеют выводить параметры женериков из того, как используется возвращаемое значение.

            Вот еще пример:
            #![allow(unused_variables)]
            fn main() {
              let guess: u32 = "42".parse().expect("Not a number!");
            }
            Ответить
            • В первом примере вывод типа не из возвращаемого значения, а из аргумента функции push.
              Ответить
              • Он имел ввиду, что выводят тип Vec::new() "из того, как используется возвращаемое значение", т.е. из аргументов, которые передают в push. Он всё правильно сказал.
                Ответить
            • А вот тут ты обломаешься: https://ideone.com/G8BJGd

              Кстати, оказалось, что вывод типа при помощи переменной, в которую сохраняется значение, есть, например, в Свифте:

              class A {
                  func f1() -> Int { return 1 }
                  
                  func f1() -> Float { return 1.1 }
              }
              
              let a = A()
              let floatArg: Float = a.f1() 
              
              print(floatArg) // 1.1.
              Ответить
              • > А вот тут ты обломаешься
                И че?
                Ответить
              • > А вот тут ты обломаешься

                Ну тут понятно, что неоднозначность, из контекста непонятно, что должно быть. Тут тебе даже хаскель тип не выведет.
                Ответить
                • Да это ясно всё, просто, зная ОП'а, можно предположить, что он хотел бы и такой вывод.

                  Кстати, с таким подходом можно сделать функции, результат которых нужно обязательно использовать (как минимум, сохранить в переменную).
                  Ответить
            • > expect ("Not a number")
              Странная логика. Вроде ждут число, а сообщение наоборот.
              Ответить
              • Это сообщение, с которым надо абортнуться, если в результате нет числа (т.е. не удалось его распарсить).
                Ответить
                • Ну вот я и говорю, что странная у них логика. Лучше назвали бы errorMessage (), чтобы мозг читателя не взрывать.
                  Ответить
      • > Можно делать возврат прокси, кастующегося во всё что требуется. Но перегрузку по вышеописанной причине не оформишь.

        template <typename T>
        struct Proxy {
            T val;
            operator auto() { return val; }
        };
        
        Proxy<double> func() { return {1.1}; }
        
        int main() {
            int a = func();
            float b = func();
            std::cout << a << " " << b << std::endl;
        }


        Например, так.
        Ответить
    • Перегрузка — говно, я предпочитаю пользоваться специализацией шаблонов с дополнительной функцией-обёрткой. Жизнь слишком коротка, чтобы разобраться, как перегрузка функций работает с шаблонами функций.
      // https://ideone.com/guycTO
      #include <cmath>
      #include <iostream>
      
      // Basic template + wrapper function for convenience
      template <typename T>
      struct CanIHazZero {
        static T zero() { return T(0); }
      };
      
      template <typename T>
      T zero() { return CanIHazZero<T>::zero(); }
      
      // Specialization
      template <>
      struct CanIHazZero<float> {
        static float zero() { return NAN; /* wow such zero very klever */ }
      };
      
      int main() {
        auto x = zero<int>();
        auto y = zero<float>();
        std::cout << "x = " << x << ", y = " << y << "\n";
        return 0;
      }
      Ответить
      • Почему бы не обойтись только специализацией самой функции?
        template <typename T>
        T zero() { return 0; }
        
        template <>
        float zero() { return NAN; }
        Ответить
        • > Почему бы не обойтись только специализацией самой функции?

          A function template can only be fully specialized
          http://www.gotw.ca/publications/mill17.htm
          Говорю же, жизнь слишком коротка, чтобы разбираться потом в этом говне.
          Ответить
        • Ну т.е. почувствуй разницу между добавлением
          template <typename T>
          CanIHazZero<std::vector<T>> {};
          и добавлением
          template <typename T>
          std::vector<T> zero() {}
          Твоя версия приведёт к появлению перегрузки, что приведёт к неоднозначности при попытке вызова функции.
          Ответить
      • > auto x = zero<int>();
        > auto y = zero<float>();

        извиняюсь, но это идиотизм. если ты уже вынуждень писать имя типа всегда, то тогда еще проще сделать:

        > auto x = zero_int();
        > auto y = zero_float();

        бонус: на букву короче, и определений в раза два меньше. красота среди бегущих.

        life is too short, to make it complicated. KISS.
        Ответить
        • > если ты уже вынуждень писать имя типа всегда, то тогда еще проще сделать

          Демонстрационный игрушечный пример же. Если тип — параметр шаблона, то не проще.
          template<typename T>
          T fast_power(const T& x, u32 n) {
            if (n == 0) return one<T>();
            if (n == 1) return x;
            if (n & 1) return mul<T>(x, fast_power(x, n - 1));
            return fast_power(mul<T>(x, x), n / 2);
          }
          Ответить
          • а, стормозил. ок. давно стл профессионально не пользовался - мозги отвыкли.
            Ответить
    • Пардон - а шо стандарт языка велит для такого поведения?
      Ответить
    • > // Почему functions that differ only in their return type cannot be overloaded

      тип возврата не включен в манглд имя функции/метода. поэтому линкер их отличить не сможет.

      не знаю почему именно было сделано.

      может быть для того что бы разрешить ап-каст типа возврата, типа "class A; class B : class A; A* getA(); B* getA() { static B b; return &b; }". или наоборот: потому что наследование классов вводит неодназначносить имени типа/класса.
      Ответить
      • > тип возврата не включен в манглд имя функции/метода

        Если функция не шаблонная.

        Whether the mangling of a function type includes the return type depends on the context and the nature of the function. The rules for deciding whether the return type is included are:

        * Template functions (names or types) have return types encoded, with the exceptions listed below.
        * Function types not appearing as part of a function name mangling, e.g. parameters, pointer types, etc., have return type encoded, with the exceptions listed below.
        * Non-template function names do not have return types encoded.

        -- https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.function-type


        > может быть для того что бы разрешить ап-каст типа возврата

        Сомневаюсь, таблице виртуальных функций наплевать на манглинг.
        Ответить
        • > > может быть для того что бы разрешить ап-каст типа возврата

          > Сомневаюсь, таблице виртуальных функций наплевать на манглинг.

          полиморфизм тут ни причем. это обычное наследование.

          это та же самая байда что и type promotion, в принципе. только с той разницей что пользовательские типы.

          > > тип возврата не включен в манглд имя функции/метода

          > Если функция не шаблонная.

          был не в курсе.

          мне лично уже бы хватило различия void/не-void функция. не универсально - но часто достаточно. (в любом случае .) стандарту бы тоже помогло: например pop_front() стлный мог бы тогда возвращать/не возвращать значение в зависимости от контекста.

          я в прошлом эксперементировал с void контекстами: пытался в выражении `obj;` заставить вызывать каст оператор `operator void ()`. деталей уже не помню - давно трахался с этим - но помню что очень криво работало. сейчас попробовал - вообще ни фига не работает. гугл говорит что все правильно - и не должно было никогда работать.
          Ответить
          • > полиморфизм тут ни причем. это обычное наследование.

            Тогда это просто разные функции, B::getA() скрывает (а не переопределяет) A::getA(). Имя класса используется в манглинге. Без переопределения можно хоть void B::getA() написать.
            Ответить
            • я не говорил про методы, а про обычные функции возвращающие объект (это мой типичный трюк в мелких компайл тестах избежать создания объекта).

              декларация: A* getA().

              определение: B* getA().

              это в крестах не работает - но теоретически могло бы.
              Ответить
              • > это в крестах не работает

                Ок, тогда непонятно, к чему ты всё это писал.
                Ответить
                • тут конкретно это был просто пример неоднозначности.
                  class A;
                  class B : public A;
                  class C : public A;
                  
                  B* getA(); // func #1
                  C* getA(); // func #2
                  
                  A* a = getA(); // подходят обе

                  то чем я в прошлом конкретно страдал - уже давно сам забыл. но было тоже в тему различия по типу возврата.
                  Ответить

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