Кресты / Говнокод #7542 Ссылка на оригинал

0

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

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

Запостил: rat4 rat4, (Updated )

Комментарии (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)
      {
      }
      Ответить
  • А 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; для установки брекпоинта в конце блока! 🙂
    Ответить
  • Кто знает, согласно нового стандарта [](...){...} - кастится до функции? Переменных то она не захватывает, а значит можно смело кастить.
    Ответить

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

Где здесь C++, guest?!

    А не использовать ли нам bbcode?


    8