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

    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
    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
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    struct Base { virtual const char *getName() = 0; virtual ~Base() = default; };
    struct SE_0 : Base { virtual const char *getName() override { return "SE_0"; } };
    struct SE_1 : Base { virtual const char *getName() override { return "SE_1"; } };
    struct SE_2 : Base { virtual const char *getName() override { return "SE_2"; } };
    
    enum TypesEnum {
        E__BEGIN = 0,
    
        E_0 = E__BEGIN,
        E_1,
        E_2,
    
        E__END
    };
    
    template<TypesEnum>
    struct Registry {};
    
    template<>
    struct Registry<E_0> {
        static constexpr const char *name = "The first type (SE_0)";
        using type = SE_0;
    };
    
    template<>
    struct Registry<E_1> {
        static constexpr const char *name = "A second type (SE_1)";
        using type = SE_1;
    };
    
    template<>
    struct Registry<E_2> {
        static constexpr const char *name = "And the last type (SE_2)";
        using type = SE_2;
    };
    
    template<TypesEnum CurrentType>
    std::unique_ptr<Base> createTypeImpl(const char *name)
    {
        if constexpr (CurrentType < E__END) {
            if (strstr(Registry<CurrentType>::name, name)) {
                return std::make_unique<typename Registry<CurrentType>::type>();
            }
            return createTypeImpl<static_cast<TypesEnum>(CurrentType + 1)>(name);
        } else {
            (void)name;  // Silence 'unreferenced formal parameter' warning
            return nullptr;
        }
    }
    
    std::unique_ptr<Base> createType(const char *name)
    {
        return createTypeImpl<E__BEGIN>(name);
    }
    
    int main()
    {
        std::cout << "first type: " << createType("first type")->getName() << std::endl;
        std::cout << "second type: " << createType("second type")->getName() << std::endl;
        std::cout << "last type: " << createType("last type")->getName() << std::endl;
    
        return EXIT_SUCCESS;
    }

    Упоролся.
    https://ideone.com/c11fz4

    Запостил: gost, 29 Апреля 2020

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

    • Когда захотел запилить рантайм-фабрику, а вменяемых метатипов не завезли.
      Ответить
    • Блин, я что-то как тот чел из хабростатьи. Смотрю на этот код, вижу треугольные скобочки, и не могу его читать.
      Ответить
      • Вот к чему крестоговно приводит!
        Ответить
      • Так ведь очевидно же все.
        Но в Ди с рефлексией проще. Тупо перечислил в цикле все имена в модуле, нашел потомков Base и создал.
        Ответить
        • А как с этим в J?
          Ответить
          • показать все, что скрытоvanished
            Ответить
            • Нидлесс тихо-мирно засыпал за щеку, но почему-то решил открыть говнокодик и теперь он пытается прочитать какое-то крестоговно.

              Проклинаю васю
              Ответить
          • Х.з. как в J, но в форте можно просто сходить да почитать словарь.
            Ответить
          • Хренево, объекты и классы в J то же, что и локали (неймспейцы по вашему, но их можно создавать в рантайме). Через жопу 4!:1 (описани https://www.jsoftware.com/docs/help807/dictionary/dx004.htm) можно получить имена в текущей локали (например 4!:1]0 получит имена существительных), но перечисляются только имена только в текущей локали, из локали которой наследуемся получить не могем, и имена родительской или унаследованной локали также получить не могем. Хоть имена локалей тем же способом (4!:1]6) и можно получить, но у них отдельный и единый неёмспейс и в списке приходят имена сразу всех имеющихся локалей. Единственное что нарыл (ну натыкал в jcоcноле): при создании объекта у него создаётся поле COCREATOR, в котором хранится имя локали в котором его создали.

            Но для того, чтобы в рантайме выбрать класс это и не нужно.
            Ответить
        • Да код несложный, просто я вижу треугольные скобочки и желание читать отпадает.
          Ответить
          • int x[] = {1 < 2, 3 > 4};
            Ответить
            • a<b, c> d;

              З.Ы. Где-то у меня был пример, который в зависимости от значения константы компилится либо как шаблон либо как операторы сравнения.
              Ответить
              • https://medium.com/@mujjingun_23509/full-proof-that-c-grammar-is-undecidable-34e22dd8b664

                > Most programming languages’ grammars fall into the category of Context-Free Grammar (CFG), or sometimes Context-Sensitive Grammar (CSG). What about C++? It turns out parsing C++’s grammar is literally undecidable. In other words, if you have a compiler that parses all valid C++ sources perfectly, you have solved the Halting Problem.

                Для написания 100% корректного компилятора C++ надо решить проблему остановки. Какой багор )))
                Ответить
                • а потом решить проблемы с пакетным менеджером, определиться с системой сборки... Тут и солнце догорит
                  Ответить
                • Error 1020
                  Ответить
              • показать все, что скрытоvanished
                Ответить
                • Маловероятно. Можно попробовать что-то подобрать с контекстно-зависимыми ключевыми словами типа override, но лень. С остальными точно не прокатит.
                  Ответить
              • Надо было им новые скобочки изобрести, а не использовать хуйню "больше-меньше". Например, через комбинацию обычных скобочек с какой-нибудь хуйней, которая не встречается. Типа @%$( как открывающая и )@%$ как закрывающая. Можно даже вводить сколько угодно видов скобочек, типа вот такого пизданатства
                @0%$( какая-то-хуйня )@0%$
                @1%$( какая-то-хуйня )@1%$
                @2%$( какая-то-хуйня )@2%$
                @3%$( какая-то-хуйня )@3%$

                и так далее. Наверняка в каком-нибудь стандарте появится и такая поебень
                Ответить
                • Да лучше уж「какие-нибудь юникодные」чем эти триграфы.
                  Ответить
                  • Тогда для их ввода надо будет какие-то хоткеи придумывать, или специальную раскладку. Да и юникод может закончиться. Еще б сделать, чтобы скобочки перегружать можно было и определять как некие новые как бы операторы, и чтоб с неймспейсами. Например, на какой-нибудь метушне можно описать что у нас теперь есть скобочки типа @0%$( и )@0%$ которые такую-то хуйню делают, и можно или делать my_namespace::@0%$( некаяхуйня my_namespace::)@0%$
                    или сделать using namespace my_namespace; и тогда просто@0%$( некаяхуйня )@0%$ писать можно. Это ж какой простор для творчества!
                    Ответить
                    • Кстати, почему в крестах сделали хуйню с using, но не сделали хуйни, чтоб этот using где-нибудь отменить? Типа
                      using namespace std;
                      
                      cout << "test\n"; // work
                      
                      unusing namespace std;
                      
                      cout << "test\n"; // doesnt work


                      Да, я знаю что можно в пределах какой-то одной функции/метода и пр. эту хуйню с юзингом сделать, но это не то.
                      Ответить
                      • Потому что кресты и так хуёво читаются, а тут ещё искать в середине файла эти using'и... Сейчас в адекватных случаях они всё-таки стоят или прям сверху файла или в начале скоупа.
                        Ответить
                      • показать все, что скрытоvanished
                        Ответить
                      • using std = delete;

                        Во! Я думаю теперь точно примут пропозал.

                        Ну и using std = default на случай если ты затёр std чем-то другим и теперь хочешь вернуть на место.
                        Ответить
                        • > Ну и using std = default на случай если ты затёр std чем-то другим и хочешь вернуть на место.

                          Лучше сделать pop(n). Типа, если несколько раз перезатер, можно на столько-то раз откатиться.

                          using std = pop(2);
                          Ответить
                          • Да и вообще, стек это говно для анскиллябр, ведь настоящие Цари знают, что единственная полезная структура данных - массив. Надо делать тьюринг-полный язык для манипуляций с массивом неймспейсов, а не просто какой-то там pop и delete.

                            Это вообще суть крестов, взять какую-нибудь тупую херню типа шаблонов, и раздуть ее до уровня маразма
                            Ответить
                            • compiletime {
                                  auto backups = std::deque<std::any>();
                              
                                  backups.push_front(std);
                                  backups.push_front(delete);
                              
                                  std = nullptr;
                                  delete = nullptr;
                              }
                              
                              // ... work without std and delete
                              
                              compiletime {
                                  delete = backups.front();
                                  backups.pop_front();
                                  std = backups.front();
                                  backups.pop_front();
                              }
                              Ответить
                          • Помнится, я такую питушню для переменных писал: https://github.com/1024--/peetooshnya/tree/master/varprepr
                            С этим препроцессором можно переиспользовать имена переменных, создавая микроскоупы. Там есть реализации для кода на C, Python или JS, можно добаалять свои.
                            Ответить
                        • Надо ещё придумать, как переиспользовать старые ключевые слова вроде static, const.
                          Ответить
                    • Какой простор )))
                      Ответить
              • > в зависимости от значения константы компилится либо как шаблон либо как операторы сравнения.

                https://govnokod.ru/26283#comment518451
                > Еще выяснилось, что я не итератор, а шаблон. Ну и черт с ним, подумал я.
                Ответить
    • А сбалансированное дерево вместо линейного поиска слабо запилить?
      Ответить
    • Что делает этот код?

      Поправь меня, если чио: createTypeImpl просматривает пачку классов, проверяя вхождение строки в поле, и когда находит, возвращает другой связанный с ним класс?
      Ответить
      • А, моя понял, в рантайме выбираеца класс и создаётся объект.
        Ответить
        • Да. В каком-нибудь я бы просто сделал что-нибудь вроде
          REGISTRY = {'first type': SE_0, 'second type': SE_1, 'last type': SE_2}
          obj = REGISTRY[name]()
          , а в кресты рантайм-метатипов не подвезли
          Ответить
          • показать все, что скрытоvanished
            Ответить
          • показать все, что скрытоvanished
            Ответить
            • Я тоже ничего не понял. Именно поэтому я за «PHP».
              Ответить
            • Создать объект, класс которого определяется строкой в рантайме.
              Юзер вводит «first type» — создаётся объект типа «SE_0»; вводит «second type» — объект типа «SE_1» и так далее.
              Ответить
              • показать все, что скрытоvanished
                Ответить
                • Реальный пример: есть N типов пакетов (сетевых), различающихся по заголовку (строка в начале). Для каждого пришедшего из сети пакета нужно создать соответствующий объект, передав в его коньструктор данные из пакета. Ситуация усугубляется тем, что заголовок отделяется от данных разными символами: где-то пробелом, где-то новой строкой, а в одном пакете — вообще обратным слэшем. Для дополнительного веселья в некоторых видах пакетов данных может вообще не быть (просто строка заголовка), а для некоторых — может быть, а может и не быть.
                  Ответить
                  • показать все, что скрытоvanished
                    Ответить
                  • показать все, что скрытоvanished
                    Ответить
                    • Да, что-то вроде этого, только вместо простого соответствия x1 -> Pituh, x2 -> Kuritsa там накостылить костылей нужно.
                      Ответить
                      • показать все, что скрытоvanished
                        Ответить
                        • Можно, но тогда возникает проблема с засовыванием в эту карту значений (это нужно сделать для каждого класса, и желательно с как можно меньшим бойлерплейтом).
                          В скриптушне я бы просто сделал декоратор а-ля @register (выше пример), а вот с крестами проблемка выходит — сделать инициализацию рантайм-карты без явного перечисления типов и строк в createObjectByName() проблематично*.

                          *Можно, но заёбисто.
                          Ответить
                          • Наскока я понимаю в сишечке (и вероятно в кристах) подход должен быть такой: если ты не хочешь писать бойлерплейт руками, и не можешь (или не хочешь по причинам перформансе) заставить рантайм сделать это за тебя, то ты используешь генерилку кода.

                            Самое страшное это писать одно и тоже знание в нескольких местах.
                            Потому в генерилке ты пишешь его ОДИН раз, а дальше оно само генерийет бойлерплейт где хоть 100500 раз это знание написино.

                            Когда я паршу текст -- я описываю грамматиики и потом лексы с бизонами за меня генерят код с говном

                            Ты пишешь парсер сетевого протокола. Это тоже самое.
                            Тебе нужно описание этого протокола в каком-то формате, где к каждому типу пакета привязан класс.

                            И геренилка кода, которая эту хуйню за тебя сгенерит.


                            ЗЫ: Я нагуглилъ
                            http://www.icsi.berkeley.edu/pubs/networking/binpacIMC06.pdf
                            https://github.com/zeek/binpac
                            Ответить
                            • Да я уже нахуевертил эту генерилку на шоблонах (по типу того, что в посте) и мокросах (намокросил табличку соответствия пакетов, в сущности, это и есть то самое описание), благо что там всего двенадцать типов пакетов есть.
                              Ответить
                        • показать все, что скрытоvanished
                          Ответить
              • показать все, что скрытоvanished
                Ответить
                • Ой, нет, я ещё не настолько ёбнулся. Лучше уж на велосипеде покататься, благо проект личный.

                  > Нативно такие штуки хорошо писать толко на скриптоговне: там это вообще всегда их коробки и хорошо работает
                  Подтверждаю.
                  Ответить
              • показать все, что скрытоvanished
                Ответить
          • Обожемой...
            Ответить
    • std::map<std::string, std::function<std::unique_ptr<base> ()>> factory = {
          { "S0", [](){ return std::make_unique<S0>();} }
          { "S1", [](){ return std::make_unique<S1>();} }
          { "S2", [](){ return std::make_unique<S2>();} }
      };
      
      factory.at(name)();
      Впизду шаблоны.
      Ответить
      • показать все, что скрытоvanished
        Ответить
        • std::map<std::string, std::unique_ptr<base>> instances = {
              { "S0", std::make_unique<S0>(); },
              { "S1", std::make_unique<S1>(); },
              { "S2", std::make_unique<S2>(); },
          };
          
          instances.at(name);
          Впизду паттерны.
          Ответить
          • показать все, что скрытоvanished
            Ответить
            • показать все, что скрытоvanished
              Ответить
            • А если сделать иерархию base <|--- pituz<S0> <|--- S0?
              Тогда
              * в base можно добавить static base *pituxes[PITUXES]; static size_t pituxes_length = 0;
              * в pituz<T> добавить статический pituz<T>::bagor, который делает
              pituxes[pituxes_length++] = new T;
              * в pituz<T> добавить виртуальную питушню pituz<T>::insert = \m -> m[тут взять имя класса у рттиуха] = this
              * бормандов instances заполнять как map (flip insert m) pituxes.

              То есть за счёт питушино-рекурсивного наследовательного паттерна прописать всем детям питушню для добавления себя в питухов на статическом этапе, а также - себя и своего имени в выбранную мапу, когда будет мапа. Когда статика отработает, и все питухи инициализируются, можно будет воспользоваться STL. В программе берём мапу и форычим питухов, прося их добавиться в мапу.

              Тогда каждый новый питух должен только унаследоваться от pitux<новый питух>, и он ватоматически добавится куда надо.
              Ответить
              • Авторегистрация - это потеря гибкости, на самом деле. Заполненная вручную мапа хоть и копипаста, но ты можешь поддерживать несколько её вариантов (для разных версий протокола, к примеру).
                Ответить
                • Я перечитал этот тред, вроде понял проблему. Видимо, вчера очень хотелось спать, и поэтому внимательно не прочитал.
                  Я всё равно не чувствую весомости потери гибкости. Получать классы по именам надо не в каждом проекте, а вот чтобы ещё несколько разных групп было - вообще редкий вариант.
                  Ответить
              • Ну и вместо массива там обычно юзают связный список, тогда количество типов знать не надо.
                Ответить
                • > потеря гибкости
                  Ну не знаю, можно тогда из pituxes нагенерировать новые питушни по вкусу.
                  Или сделать pituz<T, GROUP1 | GROUP3 | PITUXI> - какую-то битушню с группами, и автоматически распихивать по группам.
                  Хотя, я не знаю, зачем вообще весь этот пердолинг и какие подмножества питушни могут понадобиться.

                  > связный список
                  Как мне кажется, долговато кодить для статической питушни. да и массив - царская питушня.
                  Кстати, malloc может на этапе статический инициализации быть не готовым к работе?
                  Ответить
                  • Вот именно! Зачем вообще весь этот пердолинг если вручную описанная мапа проста, понятна и не нарушает SRP.

                    Плюс с мапой чётко и в одном месте видно, что именно используется. И ты получишь ошибку конпеляции если что-то из этого забыл запилить.
                    Ответить
                    • Да, но

                      >И ты получишь ошибку конпеляции если что-то из этого забыл запилить.
                      Если я забуду добавить в мапу, ошибки компиляции не будет. Хотя классы будут потомками Base и всё такое.

                      В случае с наследованием от вореционизатора я либо сразу не буду потомком Base, и компиляцию прикроют, либо буду и потомком, и сяду в мапу.
                      Ответить
                    • Мапа плоха тем, что для добавления в неё нового типа нужно открыть файл с ней (какой-нибудь PituhFactory.cpp) и явно прописать новый класс. Это, во-первых, неудобно (править один файл легче, чем два), а во-вторых, если ты забудешь добавить в мапу новый тип, то узнаешь об этом только после запуска (а если не повезёт — то после запуска у юзера).
                      Ответить
                      • Зато с одной мапой точки регистрации не рассеяны по всему проекту и легко прочитать что именно поддерживается.

                        Да и про один файл - это миф. Один хер тест какой-нибудь добавлять.
                        Ответить
                        • показать все, что скрытоvanished
                          Ответить
                        • Так с шаблоноговном это тоже легко: поддерживается только те типы, которые описаны в TypesEnum. При этом если добавить тип в TypesEnum, а зарегистрировать в Registry забыть — всё отвалится во время компиляции.
                          Ответить
              • > в pituz<T> добавить статический pituz<T>::bagor, который делает
                На этом этапе проблема. В крестах статических блоков инициализации (как в ЙАЖА, например) нетути, можно только городить ненадёжную и нестабильную питушню вроде «static auto huj = static_initialization_function()», но так возникают проблемы от порядка инициализации (я вот хз, например, будет ли инициализирован CRT на момент вызова static_initialization_function()) и до множественной инициализации (если разместить функцию в .h).
                Ответить
                • Хорошо, тогда пусть будет
                  * массив metuxes не указателей, а просто объектов (чтобы без malloc и прочей питушни)
                  * bagor с помощью placement new раскатывает в metuxes объект creator<T> с виртуальным методом который создаёт и помещает unique_ptr<T> в выбранную мапу.

                  base_creator <|--- creator<T>
                  
                  struct base_creator {
                    virtual void insert(std::map<...>) = 0;
                  };
                  
                  template <typename T>
                  struct creator {
                   void insert(... m) {
                      m[RTTIухname] = std::make_unique<T>();
                   }
                  };


                  Думаю, у всех креаторов будет размер как у указателя на vtable, поэтому их можно будет перебирать по значению и вытягивать из массива питухов реинтерпрет питухом.
                  Ответить
                  • Тогда иерархия самих классов останется как base <|-- S0, классы не будут ничем зашкварены.

                    pituxes станет char metuxes[100500] и вообще может стать статик питухом у base_creator, bagor будет делать
                    new (reinterpret_cast<creator<T>*>(metuxes) + metuxes_count++) creator<T>;
                    Ответить
                    • > bagor будет делать
                      Главный вопрос в том, как он это будет делать. Нормальных статических блоков-то не завезли, поэтому нам придётся дёргать этот код в рантайме, а для этого надо, опять же, проитерироваться по всем зарегистрированным creator<T>.
                      Ответить
                      • Багор будет делать где-нибудь в статушне, да. Я наврал про то, что base <|-- S0 и запутался.

                        Так и будет base <|--- pituz<S0> <|--- S0.

                        У pituz<S0> будет статический экземпляр pituz<S0>::creator_for_creator, у которого в конструкторе будет
                        new (reinterpret_cast<creator<T>*>(metuxes) + metuxes_count++) creator<T>;

                        creator<T> имеет виртуальный метод для создания T.


                        Хотя, давай по новой.
                        Вместо него можно использовать
                        template <typename T> void put_me(std::map<..., uniq...<Base>>& your_map);

                        Эта питушня будет создавать unique_ptr на новый объект и складывать его в мапу your_map.

                        metuxes создать как простой массив указателей на функции типа void (*)(std::map<..., uniq...<Base>>&);

                        У pituz<T> будет статический экземпляр pituz<T>::creator_for_creator, у которого в конструкторе будет metuxes[metuxes_lenth++] = put_me<T>;

                        А дальше будет функция fill_your_map, которая будет брать metuxes и вызывать их на переданной мапе в нужном месте.

                        При создании нового класса надо будет наследовать его от pituz<этот класс> и всё.

                        А там, где нужна мапа - делать std::map<.....> m; fill_your_map(m);

                        Вроде всё упавшее снова разложил по полочкам.
                        Ответить
                        • А какая была бизнес-задача? И была ли? Может это случай XY проблемы? По-любому надо было для 5 классов решить.
                          Ответить
                          • Там сверху написано было, посмотрите, начиная с http://govnokod.ru/26621#comment543942. Парсер, дюжина подпарсеров, выбор нужного по заголовку.
                            Ответить
                        • Ничего не понял, что будет делать put_me() и как будет реализована createObject(string name)?
                          Ответить
                        • Нахуевертил поебень по мотивам, которая должна регистрировать тип, отнаследованный от BaseRegistrator<T> (какой «CRTP», кстати!): https://wandbox.org/permlink/0RWVZIDjQTYNjbvU. Есть две проблемы:
                          1. Статический член (в твоей нотации — creator_for_creator) не инициализируется, если к нему явно не обратиться.
                          2. Этот же самый код, собранный через «clang», падает с сегфолтом. «Gcc» и «MSVC» работают (но согласно первому пункту).

                          Собственно, как я и говорил в верхних комментах, статическая иницализация в крестах — говно ёбанное. То ли дело ЙАЖА.
                          Ответить
                          • > статическая инициализация - говно ебаное

                            Которая из её форм? Надо ещё парочку добавить чтобы пофиксить.
                            Ответить
                          • показать все, что скрытоvanished
                            Ответить
                          • Есть опасение, что ДОБРОЕ ОПРЕДЕЛЕНИЕ ДОБРАЯ ПЕРЕМЕННАЯ ИДЕНТИФИКАТОР ИДЕНТИФИКАТОРЕК В ЧЕСТИ Главная карта может как-то плохо себя вести. У меня был пост про выстрел в ногу со статушнёй:
                            http://govnokod.ru/15263

                            Поэтому я предлагал изначально на всякий случай питушить в простой массив.

                            > что будет делать put_me()
                            Создавать объект и добавлять в бормандову мапу
                            template <T>
                            void put_me(std::map<std::string, std::unique_ptr<T>>& m) {
                              m[T::name()] = std::make_unique<T>();
                            }


                            > и как будет реализована createObject(string name)?
                            return instances.at(name);

                            где instances - бормандова питушня из второго комментария.
                            > А там, где нужна мапа - делать std::map<.....> m; fill_your_map(m);
                            std::map<.....> instances; fill_your_map(instances);
                            Ответить
                            • > Создавать объект и добавлять в бормандову мапу
                              Дык в мапу надо добавлять не объект, а функцию, создающую объект.

                              > return instances.at(name);
                              Дык нужны не синглтоны, а обычные объекты.

                              > Есть опасение
                              Дык не опасение, а реальность — оно вообще нихрена не работает.
                              Ответить
                              • https://stackoverflow.com/questions/6420985/how-to-force-a-static-member-to-be-initialized
                                Тут предлагают вставить в BaseRegistrator<>
                                static_assert(&creator);

                                Если попердолить это выражение, компиляторы перестают выдавать предупреждение, а код всё ещё работает.

                                Перенёс пердолинг с STL и прочей питушнёю сложнее царского массива в main, как и планировал.
                                Правда, я что-то не то сделал, и код со string_view сломался - заменил на string.

                                Вот, в итоге заставил работать эту питушню:
                                https://wandbox.org/permlink/KBmYlLmfVWyInOxk
                                Из-за синглтонства пришлось unique_pitux заменить на shared_pitux, сделал поэтому вспомогательный pointer<T>

                                > надо добавлять не объект, а функцию, создающую объект.
                                Хорошо, не будем слушать восьмишка, это тоже можно: https://wandbox.org/permlink/WT9nQqCZJfBPhcIb
                                Ответить
                                • Годно!

                                  Со static_assert, впрочем, выглядит как крайне ненадёжный хак, который сломается в следующей мажорной версии. Именно поэтому я против «C++».
                                  Ответить
                            • put_me()
                              and then just map me
                              till I can fill your registration
                              Ответить
              • ура, виртуальная петушня
                Ответить

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