Question

I'm thinking about migrating my code toward using C++11-style lambdas instead of having binds everywhere. But I'm not sure if it's a good idea or not.

Does using e.g. boost::lambda (or boost::phoenix) have any practical advantage over C++11-style lambdas?

Is it a good idea to move over to lambdas? Should I migrate my code or not?

Was it helpful?

Solution 3

Yes: It can (sometimes) significantly affect the output sizes.

If your lambdas are different from each other in any way, they will generate different code, and the compiler will likely not be able to merge the identical parts. (Inlining makes this a lot harder.)

Which doesn't look like a big deal when you first look at it, until you notice:
When you use them inside templated functions like std::sort, the the compiler generates new code for each different lambda.

This can blow up the code size disproportionately.

bind, however, is typically is more resilient to such changes (although not immune to them).

To illustrate what I mean...

  1. Take the example below, compile it with GCC (or Visual C++), and note the output binary size.
  2. Try changing if (false) to if (true), and seeing how the output binary size changed.
  3. Repeat #1 and #2 after commenting out all except one of the stable_sorts in each part.

Notice that the first time, C++11 lambdas are slightly smaller; after that, their size blows up after each use (about 3.3 KB of code for each sort with VC++, similar with GCC), whereas the boost::lambda-based binaries barely change their sizes at all (it stays the same size for me when all four are included, to the nearest half-kilobyte).

#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; });
    }
}

Note that this is "trading size for speed"; if you're in a very very tight loop, it can involve an extra variable (because now it's using pointers-to-members).
However, this is nothing like the overhead std::function introduces (which is a virtual call), and even that is unmeasurable in many cases, so this shouldn't be a cause for concern.

OTHER TIPS

The main advantage would be polymorphic functors. Currently, C++11 lambdas are monomorphic, i.e., they only take single argument type, whereas bind() allows you to create functors that accept any argumen type as long as the bound functor is callable with it.

#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
}

Yes, Boost lambdas are polymorphic, C++11 lambdas are not. That means that, for example, you cannot do it with C++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);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top