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...
- Take the example below, compile it with GCC (or Visual C++), and note the output binary size.
- Try changing
if (false)
to if (true)
, and seeing how the output binary size changed.
- Repeat #1 and #2 after commenting out all except one of the
stable_sort
s 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.
bind
. But I posted this question (and answer) to point out that even if you're in a situation where they are both readable and usable (i.e. even if you don't need polymorphism, and even if readability isn't an issue), there are still reasons to choosebind
. – Dreamland