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

    +1

    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
    #include <cstdio>
    
    class tag;
    
    template<class>
    struct type { friend constexpr auto get(type); };
    
    template<class TKey, class TValue>
    struct set { friend constexpr auto get(TKey) { return TValue{}; } };
    
    void foo() {            // never called
      if constexpr(false) { // never true
        if (false) {        // never true
            constexpr auto call = [](auto value) { std::printf("called %d", value); };
            void(set<type<tag>, decltype(call)>{});
        }
      }
    }
    
    int main() {
      get(type<tag>{})(42); // prints called 42
    }

    https://twitter.com/krisjusiak/status/1186363017329594368
    Какой C++20 )))

    Запостил: j123123, 21 Октября 2019

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

    • Попробуем разобраться. Обычно, когда пытаешься объяснить, сам начинаешь понимать

      #include <cstdio>
      
      class tag; // объявляем класс
      // Класс без тела? Часто ли нужны такие классы?
      // НАХУЯ может понадобиться класс, который содержит НИХУЯ и делает НИХУЯ??
      // ну да похуй, идём дальше
      
      template<class> // шоблонная структура, обычное дело
      // а нет, погодите, 
      // шоблонный тип нигде не используется
      // НАХУЯ тогда шаблон??
      // не понятно
      struct type { 
         friend constexpr auto get(type); 
         // метод без реализации (ну а нахуй она нужна?),
         // принимающий аргумент типа как и сама структура
         // возвращает хуй знает что. Как вообще конпелятор понял какой тип вернуть?
      };
      
      template<class TKey, class TValue> // обычный шаблон, тут ничего необычного
      // хотя постойте....
      // а ну да, пока всё ОК
      struct set {
          friend constexpr auto get(TKey) { return TValue{}; }
          // тут хотя бы понятно, что возвращаемый тип - TValue
      }; // эта структура понятная
      
      void foo() {            // never called
        if constexpr(false) { // never true
          if (false) {        // never true
              // лямбда, принимающая инт (судя по %d)
              constexpr auto call = [](auto value) { std::printf("called %d", value); };
              // создаём экземпляр класса, где
              // TKey = type<tag> (помним, что tag - это нихуя, type - обёртка над нихуя, не используящая это нихуя)
              // TValue = function<void(int)>, я ж не ошибся?
              void(set<type<tag>, decltype(call)>{});
              // приводим к воиду, кажется
              // а может это объявление функции?
          }
        }
      }
      
      int main() {
        // вызываем функцию get
        // но у нас нет ни функции get, ни класса get
        // чо за хуйня?
        get(type<tag>{})(42); // prints called 42
      }


      но нет
      Ответить
    • Джей один два три один два три, пойдёшь с нами в бар?
      Ответить
    • g++ 9.2.1:

      test.cpp:6:45: warning: friend declaration ‘constexpr auto get(type< <template-parameter-1-1> >)’ declares a non-template function [-Wnon-template-friend]
          6 | struct type { friend constexpr auto get(type); };
            |                                             ^
      test.cpp:6:45: note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) 
      test.cpp: In instantiation of ‘constexpr auto get(type<tag>)’:
      test.cpp:21:18:   required from here
      test.cpp:9:62: error: use of deleted function ‘foo()::<lambda(auto:1)>::<lambda>()’
          9 | struct set { friend constexpr auto get(TKey) { return TValue{}; } };
            |                                                              ^
      test.cpp:14:32: note: a lambda closure type has a deleted default constructor
         14 |         constexpr auto call = [](auto value) { std::printf("called %d", value); };
            |                                ^
      test.cpp: In function ‘int main()’:
      test.cpp:21:6: error: void value not ignored as it ought to be
         21 |   get(type<tag>{})(42); // prints called 42
            |   ~~~^~~~~~~~~~~~~


      Хотя всё равно непонятно, какого чёрта он пытается использовать эту лямбду внутри foo.
      Ответить
      • Действительно, у лямбды до C++20, если её использовать в качестве класса, конструктор помечен как deleted. Я не знаю, как это исправить.

        Переписал без лямбды:
        #include <cstdio>
        
        class tag;
        
        template<class>
        struct type { friend constexpr auto get(type); };
        
        template<class TKey, class TValue>
        struct set { friend constexpr auto get(TKey) { return TValue{}; } };
        
        struct pituh { auto operator ()(auto value) { std::printf("called %d", value); } };
        
        void foo() {            // never called
          if constexpr(false) { // never true
            if (false) {        // never true
                constexpr auto call = pituh{};
                void(set<type<tag>, decltype(call)>{});
            }
          }
        }
        
        int main() {
          get(type<tag>{})(42); // prints called 42
        }


        https://ideone.com/CKQgWG

        Всё равно лезет в блок, который внутри if (false), который внутри if constexpr(false), который внутри void foo(), которая никогда не вызывается.
        Ответить
      • Вот корректная версия, не требующая C++20:
        #include <cstdio>
        
        class tag;
        
        template<class>
        struct type { friend constexpr auto get(type); };
        
        template<class TKey, class TValue>
        struct set { friend constexpr auto get(TKey) { return TValue{}; } };
        
        void foo()
        {
          if constexpr(false) {
            if(false) {
                    
              class pituh
              {
                public: 
                inline /*constexpr */ void operator()(int value) const {
                  printf("called %d", value);
                }
                private: 
                static inline void __invoke(int value) {
                  printf("called %d", value);
                }
              };
              
              constexpr const auto call = pituh{};
              void(set<type<tag>, decltype(call)>{});
            }
          }
        }
        
        int main()
        {
          get(type<tag>{})(42);
        }
        Ответить
      • От лямбды ещё можно наследоваться:
              constexpr auto lambda = [](auto value) { std::printf("called %d", value); };
        
              class pituh: public decltype(lambda) {
                public: 
                pituh() {};
              };
        
              void(set<type<tag>, pituh>{});

        Но в компиляторах до C++20 выкинет с сообщением об ошибке:
        e.cpp:20:17: error: use of deleted function 'foo()::<lambda(auto:1)>::<lambda>()'
                 pituh() {};
                         ^

        Конструктор с атрибутом deleted перекрыть не получается. Либо это невозможно, либо я слишком анскилльный.
        Ответить
        • Починил:
          #include <cstdio>
          
          class tag;
          
          template<class>
          struct type { friend constexpr auto get(type); };
          
          template<class TKey, class TValue>
          struct set { friend constexpr auto get(TKey) { return TValue::bar(); } };
          
          void foo()
          {
            if constexpr(false) {
              if(false) {
                      
                constexpr auto lambda = [](auto value) { std::printf("called %d", value); };
          
                class pituh: public decltype(lambda) {
                  public: 
                  static decltype(lambda) bar() {return lambda;};
                };
          
                set<type<tag>, pituh>{};
              }
            }
          }
          
          int main()
          {
            get(type<tag>{})(42);
          }


          Заменил вызов конструктора вызовом статического метода. Компилируется в C++17 (если бы не if constexpr, можно было бы ещё понизить версию стандарта).
          Ответить
          • Если убрать if constexpr, оставив только if(false), то компилируется в C++14 и работает так же. Даже с -O2: оптимизация выкидывает все вызовы промежуточных функций, перенося вызов printf прямо в main.
            Ответить
    • Разбор оригинального кода (требует поддержки C++2a из-за потребности в конструкторе лямбды, который недоступен даже в C++17):
      https://cppinsights.io/s/6a95dc50
      Ответить
    • Кстати, что-то я подзабыл, компайлтайм переменные на основе подобной хуеты с френдами пилили?
      Ответить
      • У меня про это был говнокод https://govnokod.ru/24542

        Ну и вот еще https://habr.com/ru/post/268141/ https://stackoverflow.com/questions/44267673/is-stateful-metaprogramming-ill-formed-yet
        Ответить

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