Имеет ли bind() какое-либо преимущество (кроме совместимости) над С++ 11 lambdas?

Я подумываю о переносе моего кода на использование lambdas в стиле С++ 11, а не везде bind. Но я не уверен, хорошая идея или нет.

Использует ли, например, boost::lambda (или boost::phoenix) имеют какое-либо практическое преимущество перед lambdas в стиле С++ 11-стиля?

Хорошо ли переходить к лямбдам? Должен ли я переносить свой код или нет?

Ответы

Ответ 1

Да: он может (иногда) значительно влиять на размеры вывода.

Если ваши лямбды отличаются друг от друга по любому, они будут генерировать другой код, и компилятор, вероятно, не сможет объединить идентичные части. (Inlining делает это намного сложнее.)

Что не похоже на большое дело, когда вы сначала смотрите на него, пока не заметите:
Когда вы используете их внутри шаблонных функций, таких как std::sort, компилятор генерирует новый код для каждого другого lambda.

Это может привести к непропорциональному увеличению размера кода.

bind, однако, как правило, более устойчив к таким изменениям (хотя и не иммунитет к ним).

Чтобы проиллюстрировать, что я имею в виду...

  • Взять пример ниже, скомпилировать его с помощью GCC (или Visual С++) и отметить выходной двоичный размер.
  • Попробуйте изменить if (false) на if (true) и посмотреть, как изменился размер двоичного файла.
  • Повторите # 1 и # 2 после комментирования всех, кроме одного из stable_sort в каждой части.

Обратите внимание, что в первый раз, С++ 11 lambdas немного меньше; после этого их размер взрывается после каждого использования (около 3,3 КБ кода для каждого сорта с VС++, аналогично GCC), тогда как двоичные файлы на основе boost::lambda практически не меняют своих размеров (он остается для меня таким же, все четыре включены, с точностью до половины килобайта).

#include <algorithm>
#include <string>
#include <vector>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/lambda.hpp>   // can also use boost::phoenix

using namespace boost::lambda;

struct Foo { std::string w, x, y, z; };

int main()
{
    std::vector<Foo> v1;
    std::vector<size_t> v2;
    for (size_t j = 0; j < 5; j++) { v1.push_back(Foo()); }
    for (size_t j = 0; j < v1.size(); j++) { v2.push_back(j); }
    if (true)
    {
        std::stable_sort(v2.begin(), v2.end(), bind(&Foo::w, var(v1)[_1]) < bind(&Foo::w, var(v1)[_2]));
        std::stable_sort(v2.begin(), v2.end(), bind(&Foo::x, var(v1)[_1]) < bind(&Foo::x, var(v1)[_2]));
        std::stable_sort(v2.begin(), v2.end(), bind(&Foo::y, var(v1)[_1]) < bind(&Foo::y, var(v1)[_2]));
        std::stable_sort(v2.begin(), v2.end(), bind(&Foo::z, var(v1)[_1]) < bind(&Foo::z, var(v1)[_2]));
    }
    else
    {
        std::stable_sort(v2.begin(), v2.end(), [&](size_t i, size_t j) { return v1[i].w < v1[j].w; });
        std::stable_sort(v2.begin(), v2.end(), [&](size_t i, size_t j) { return v1[i].x < v1[j].x; });
        std::stable_sort(v2.begin(), v2.end(), [&](size_t i, size_t j) { return v1[i].y < v1[j].y; });
        std::stable_sort(v2.begin(), v2.end(), [&](size_t i, size_t j) { return v1[i].z < v1[j].z; });
    }
}

Обратите внимание, что это "торговый размер для скорости"; если вы находитесь в жестком цикле очень сильно, он может включать дополнительную переменную (потому что теперь она использует указатели для членов).
Однако это ничего не значит, что накладные расходы std::function вводят (что является виртуальным вызовом), и даже это неизмеримо во многих случаях, поэтому это не должно вызывать беспокойства.

Ответ 2

Основным преимуществом было бы полиморфные функторы. В настоящее время Lambdas С++ 11 мономорфны, т.е. Они принимают только один тип аргумента, тогда как bind() позволяет создавать функторы, которые принимают любой тип argumen, если связанный функтор может быть с ним связан.

#include <functional>

struct X{
  template<class T, class U>
  void operator()(T, U) const{}
};

int main(){
  X x;
  auto l_with_5 = [x](int v){ return x(v, 5); };
  auto b_with_5 = std::bind(x, std::placeholders::_1, 5);
  l(4);
  b("hi"); // can't do that with C++11 lambdas
}

Ответ 3

Да, Boomb lambdas являются полиморфными, С++ 11 lambdas - нет. Это означает, что, например, вы не можете сделать это с С++ 11 lambdas:

template<class T>
void f(T g)
{
    int x = 123;
    const char* y = "hello";
    g(x); // call with an integer
    g(y); // call with a string
}

int main() {
    f(std::cout << _1);
}