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

    +170

    1. 1
    [](){}();

    Поздравляю с новым стандартом, товарищи!

    Запостил: rat4, 14 Августа 2011

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

    • Похоже на серию из Сауспарка, где Каил никак не хотел прочитать пользовательское соглашение от Айпада.
      Ответить
    • показать все, что скрытоЛучше.
      ( ( ) ( ( ) ) ( ) )
      Ответить
    • Алгоритмы stl не удобно использовать без лямбд.

      Приходится писать, что-то типа:
      vector<int> container;
      float var;
      struct _
      {
        static void lol(int item, float* var)
        {
          //обрабатываем элемент и используем *var
        }
      };
      for_each(container.begin(), container.end(), bind2nd(ptr_fun(&_::lol), &var))
      Притом уродский bind2nd не хочет принимать ссылки на var(приходится использовать указатель), хотя мог бы. И ptr_fun приходится использовать, хотя он тут совсем не нужен.

      Или так пишем в старом стандарте, что ещё более громоздко:
      vector<int> container;
      float var;
      struct _
      {
      float& Var;
        _(float& var):
           Var(var)
        {};
        void operator()(int item)
        {
          //обрабатываем элемент и используем Var
        }
      };
      for_each(container.begin(), container.end(), _(var))


      А вот в новом стандарте такой проблемы нет:

      vector<int> container;
      float var;
      for_each(container.begin(), container.end(), 
        [&var](int item)
        {
           //обрабатываем элемент и используем var
        }
      )

      Это куда читабельнее, меньше писать и удобнее.

      Хотя было бы хорошо иметь вывод типов, но и так сойдет, ибо хоть что-то. :)
      Ответить
      • Кстати, уродские bind2nd и иже с ними отлично заменяются универсальным boost::bind, который также успешно перекочевал в новый стандарт в виде std::bind. В MS VS2010 уже видел. Правда, при попытках использования в коде одновременно буста и его новых стд-аналогов пока что временами лезут конфликты - бустовцы что-то в неймспейсы недовынесли...

        А ещё есть boost::function (он же отныне std::function). Незаменимый инструмент для создания методов, итерирующих некоторый инкапсулированный контейнер с вызовом функтора (с заданной сигнатурой, но совершенно произвольным типом) на каждом элементе. Альтернатива - только шаблонный метод, со всеми его недостатками вроде нестрогой типизации и необходимости реализации в заголовочном файле.
        Ответить
        • >boost::function
          Можно привести пример использования boost::function в данной области, как замена биндигу/каррированию?

          >std::function
          >std::bind
          Это есть только в новом стандарте, так что это лишь повод его "восхвалять".
          Ответить
          • Это не замена биндингу, это дополнительный инструмент для применения вместе с ним.

            Пример:

            typedef boost::function<void(SceneNode&)> FunctorNodeRef;
            void traverse(const FunctorNodeRef& functor);

            Метод класса SceneNode, являющегося, собственно, узлом графической сцены. traverse проходит по всему поддереву сцены, начинающемуся с данного узла, и для каждого узла в поддереве вызывает функтор.

            Вызов может выглядеть так:

            void SomeClass::foo(SceneNode& node) {}

            void SomeClass::bar()
            {
            node.traverse(boost::bind(&SomeClass::fo o, this, _1));
            }

            А может и так:

            void globalFoo(SceneNode& node) {}

            void SomeClass::bar()
            {
            node.traverse(globalFoo);
            }

            Один и тот же метод подходит для обоих вариантов.

            > Это есть только в новом стандарте,
            > так что это лишь повод его "восхвалять".
            Я вроде бы и не говорил, что новый стандарт плох. Наоборот, рад, что он заимствует по-настоящему полезные вещи хотя бы из того же буста.
            Ответить
      • >>Алгоритмы stl не удобно использовать без лямбд.
        Поправка: алгоритмы STL удобнее использовать с лямбдами.
        Ответить
    • Ещё новый стандарт решает проблему кода, типа:
      for (std::map<unsigned int, std::string, MyClass::MyAllocator,  куча_?нужной?_шаблонной_хуйни>::const_iterator it = v.begin(); it!=v.end(); ++it)

      Заменяя это на:
      for (auto it = v.begin(); it!=v.end(); ++it)
      Ответить
      • Эта проблема и раньше неплохо решалась тайпдефами. У нас в кодстайле, например, объявляя мембер класса, являющийся каким-нибудь стл-ным или бустовым контейнером, обязательно рядом делать тайпдеф на этот контейнер. Обычно совпадающий по названию с именем мембера (отличающийся регистром и ещё одной мелочью). Ну и для не-мемберов никто не запрещает это же делать. Т.е., например:

        typedef std::vector<std::string> Strings;
        Strings strings;
        ...
        for (Strings::iterator it = strings.begin(); it != it.end(); ++it)
        {
        ...
        }

        Ещё, если не нужны именно итераторы (например, для удаления), есть вот такая (пускай и не стандартная) штука:

        BOOST_FOREACH(const std::string& str, strings)
        {
        ...
        }

        Разворачивается он, конечно в нечто адское (не знаю насчёт оптимальности), но если скорость не критична, то читабельность важнее, а оптимизациями займётся компилятор.

        Короче, кто хотел решать проблемы - решил их для себя давно. Но auto - это тоже хорошо, не спорю. А лямбды - вообще отлично.
        Ответить
        • >Strings::const_iterator
          Всё равно это длинно.


          >Разворачивается он, конечно в нечто адское (не знаю насчёт оптимальности), но если скорость не критична

          Скорость выполнения не страдает. Скорость компиляции - запустил на перекомпиляцию и пошёл играть в домино:
          http://xkcd.ru/i/303_v1.png
          Ответить
          • Ну для циклов по итераторам auto лучше, да. Хуже будет, когда некоторые начнут писать через него вообще весь код, потому что иначе "длинно". :)

            Скорость компиляции - это да. "Технологичный" код от этого страдает по определению. Помнится, позаимствовали как-то из того же буста библиотеку для стейт-машин. Почти всё выглядело красиво и удобно, но компилировалось под конец проекта минут 20-25 на машине с четырёхядерным процом и парой гигов оперативки.
            На следующем проекте заимствовали только идеи. Самопальная "облегченная и усовершенстованная" либа работает в нашем контексте не хуже (даже лучше), компилируется при тех же масштабах в 4-5 раз быстрее. Что, конечно, тоже многовато. Но что делать - прогресс же.
            Ответить
            • >Хуже будет, когда некоторые начнут писать через него вообще весь код, потому что иначе "длинно"

              У всех методов и систем есть отрицательные стороны. Главное не в том, для чего эти методы и системы можно использовать, а в том, как их применяют.

              Используя атомные исследования, не обязательно создавать ядерную бомбу, у них есть и мирные применения.

              auto всеж лучше, чем any или dynamic.
              Ответить
            • С учётом существования языков с проверкой типов во время выполнения, типа PHP или Python, то auto - миленький положительный персонаж.
              Ответить
            • Во всех новых языках аналог auto уже давно есть, например var в C# и почему-то никто не волнуется, что им будут пользоваться на право и налево.
              Ответить
            • Да уж, за что не люблю C++, так это за скорость компиляции. Java-проект из 500+ классов компилится меньше, чем за минуту.
              На моей генту компиляция chromium с его WebKit - дело довольно долгое и унылое, занимающее несколько часов. Ядро же компилится 20 минут.
              Ответить
              • Хорошо когда только компилится. А может же ещё потом хрен знает сколько времени линковаться, вообще не показывая никакого прогресса, кроме "linking..."
                Ответить
                • да-да, так и есть, напрягает. Сишные проги как-то поприятней собирать.
                  Ответить
              • > Ядро же компилится 20 минут.
                у меня минуты 2-3 забирает
                HINT:core2duo 2,4Ghz + ccache
                Ответить
              • Возьми из шкафа огнетушитель. Припекает уже.
                Ответить
      • если что новый стандарт заменяет это на
        for (auto it : v)
        {
        }
        Ответить
        • Во что это будет разворачиваться?
          Ответить
          • RTFM:
            http://www2.research.att.com/~bs/C%2B%2B0xFAQ.html#for
            Ответить
            • Спасибо. Ясно. А вот в случае со стандартными массивами типа "int arr[100];" это как будет работать? begin() и end() у них нет.
              Ответить
              • Ну так фича языка же. Как компилятор реализуют - так и будет работать. Тип же компилятору известен, так что массив и список инициализации вполне могут им обрабатываться отдельно.
                Ответить
    • А rvalue ссылки - типичная для ниши С++, ручная оптимизация кода. С применением этой оптимизации - нет проблемы лишнего копирования больших данных возвращаемого значения из функций и операторов, критичных ко времени выполнения.
      Ответить
      • Я вдруг понял, что они не особо нужны. Достаточно перегружать операторы и функции, что-бы те возвращали враппер, а не оригинальные типы, участвующие в операции. А дальше мы делаем перегрузку отдельно для враппера и отдельно для оригинального типа. И дальше, в тех случаях, если нам попался враппер - это rvalue и значит можно смело использовать эту оптимизацию. :)
        Так что без rvalue-ссылок можно обойтись.
        Ответить
        • Matrix b, c;
          Matrix a(b + c);

          Где именно будет ждать сама матрица результата, пока конструктор не проинициализируется враппером? Нет, можно, конечно, саму матрицу засунуть в динамическую память и использовать подсчёт ссылок, но это будет очень неэффективно.
          Ответить
          • Компилировать не пробовал, но что-то типа такого:
            using namespace std;
            
            struct ValueWrapper;
            
            struct Value
            {
            	int data[10000];
            
            	const ValueWrapper operator+(const Value& r) const;
            	const ValueWrapper& operator+(const ValueWrapper& r) const;
            };
            
            struct ValueWrapper: public Value
            {
            	operator Value&(){return *this;}
            	operator const Value&()const{return *this;}
            };
            
            const ValueWrapper Value::operator+(const Value& r) const
            {
            	ValueWrapper result;
            	transform(begin(data), end(data), begin(r.data), result.data, plus<int>());
            	return result;
            }
            
            const ValueWrapper& Value::operator+(const ValueWrapper& r) const
            {
            	ValueWrapper& result=const_cast<ValueWrapper&>(r);
            	transform(begin(data), end(data), begin(result.data), result.data, plus<int>());
            	return r;
            }
            int _tmain(int argc, _TCHAR* argv[])
            {
            	Value a, b, d;
            	Value c=a+b+d;


            Для случая rvalue (а именно ValueWrapper) будет вызываться более быстрый оператор + без лишнего копирования для возвращаемого значения, тк мы используем временную переменную rvalue, которую можно безвозбранно портить, тк она временная и больше никому не нужна. .
            Ответить
            • Fixed. Проверил, вроде работает:
              using namespace std;
              
              struct ValueWrapper;
              
              struct Value
              {
              	int data[10000];
              	const ValueWrapper operator+(const Value& r) const;
              };
              
              struct ValueWrapper
              {
              	 Value value;
              	operator Value&(){return this->value;}
              	operator const Value&()const{return this->value;}
              	const ValueWrapper operator+(const Value& r) const;
              	//const ValueWrapper& operator+(const ValueWrapper& r)const;
              
              };
              
              const ValueWrapper& ValueWrapper::operator+(const Value& r) const
              {
              	ValueWrapper& result=const_cast<ValueWrapper&>(*this);
              	transform(begin(value.data), end(value.data), begin(r.data), result.value.data, plus<int>());
              	return result;
              }
              
              const ValueWrapper Value::operator+(const Value& r) const
              {
              	ValueWrapper result;
              	transform(begin(data), end(data), begin(r.data), result.value.data, plus<int>());
              	return result;
              }
              
              /*const ValueWrapper& ValueWrapper::operator+(const ValueWrapper& r)const
              {
              	ValueWrapper& result=const_cast<ValueWrapper&>(r);
              	transform(begin(value.data), end(value.data), begin(result.value.data), result.value.data, plus<int>());
              	return r;
              }*/
              
              
              int _tmain(int argc, _TCHAR* argv[])
              {
              	Value a, b, d;
              	auto e=(a+b)+(a+b)+d;
              	auto f=(a+b)+(a+b)+d;
              	Value c=e+f;
              Вывод: без RValue&& ссылок, как инструмента оптимизации, можно обойтись.
              Ответить
              • Исправление для 13ой строчки:
                const ValueWrapper& operator+(const Value& r) const;
                Ответить
              • Да, вот это уже более работающее. Но:
                1) Нужно реализовывать дубликат для каждой операции, каждого метода.
                2) Всё равно будет копирование результата в конечную переменную. Практически то же самое было бы и без враппера.
                3) При умножении, а не сложении, матриц преимуществ не будет, а недостатки останутся.
                Ответить
                • >Да, вот это уже более работающее.
                  Я так и не понял, почему первоначальная не заработала... :(
                  Ответить
                  • Потому, что всегда использовался Value::operator+(const Value& r) — правое слагаемое Value, и operator+ определён только у Value.
                    Ответить
                    • Сейчас вы сказали и я понял. Только дело не в правом слагаемом, а в левом. Левое всегда вычисляется первее правого и по нему и происходила "перегрузка" (в данном случае полиморфизм). А operator+ присутствовал только в Value и "полиморфизма не происходило".
                      Хотя вы, наверное, это и сказали.

                      Спасибо. :)
                      Ответить
                      • И в правом, и в левом. Поскольку operator+ определён только у Value, то вызывается Value::operator+ (левый аргумент приводится к Value). А поскольку правый аргумент Value, то Value::operator+(const ValueWrapper& r) вообще не у дел.
                        Ответить
                • 1) Это есть и после введения RValue-ссылок.
                  2) От этого можно как-то уйти? В случае RValue-ссылок такой проблемы разве не будет?
                  3) Это есть и после введения RValue-ссылок.
                  Ответить
                  • 1) Нет, ведь нет класса-враппера, у которого определяются дубликаты. См. отличия первого (нерабочего) и второго вариантов.
                    2) Конструкторы и присваивания с семантикой move — то, зачем rvalue-ссылки и введены.
                    3) Для подобного типа достаточно RVO. А вот были бы данные в динамической памяти, была бы выгода от move (и без затрат на счётчик ссылок).
                    Ответить
                    • Похоже, не до конца я понимаю эрвалуе ссылки... Нужно исходники стандартной библиотеки по изучать. Там то это ввели повсеместно. Покопаюсь и авось до конца разберусь. Спасибо. :)

                      >то, зачем rvalue-ссылки и введены.
                      А я думал их для оптимизации ввели, а move-семантика - побочный эффект. :-[
                      Ответить
                      • Ну так move тоже — это такая оптимизация. Т.е. можно и без rvalue-ссылок, и без move, но либо писать придётся намного больше (причём совершенно тупого кода), либо будет лишний оверхед.
                        Ответить
    • Кстати, отличная замена int i = 0; или _asm nop; для установки брекпоинта в конце блока! :)
      Ответить
    • Кто знает, согласно нового стандарта [](...){...} - кастится до функции? Переменных то она не захватывает, а значит можно смело кастить.
      Ответить
    • CPP0xArt?
      Ответить

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