While refactoring some legacy code, I came across this traditional implementation of a predicate to be used in STL algorithms:
template<bool b>
struct StructPred {
bool operator()(S const & s) { return s.b == b; }
};
I was tired and hitting the Ballmer Peak, so I accidentally rewrote it into a lambda like this, which seemed natural and also worked:
template<bool b>
auto lambda_pred = [] (S const & s) { return s.b == b; };
Later I realized that I've never seen a template lambda like this. I couldn't find anything similar on cppreference or on stackoverflow. The canonical way of producing template lambdas seems to be wrapping them in template structs or template functions. C++20 introduces named template params for lambdas, but that's a different syntax (after the capture brackets).
Now my questions are: Is this legal syntax? Is it documented anywhere? Is it even a lambda or something else? Are there any implications or side effects as compared to the wrapper alternatives? Why does everybody recommend wrapper implementations when this works? Am I missing something obvious?
Full working test code below and at godbolt. Just to be sure I also added a type template parameter version. MSVC, GCC and clang are happy with this code.
#include <vector>
#include <algorithm>
struct S {
bool b = false;
};
// classic function object
template<bool b>
struct StructPred {
bool operator()(S const & s) { return s.b == b; }
};
// template function producing a lambda
template<bool b>
auto make_pred() {
return [] (S const & s) { return s.b == b; };
}
// direct template lambda
template<bool b>
auto lambda_pred = [] (S const & s) { return s.b == b; };
// also with type params
template<typename T, bool b>
auto lambda_pred_t = [] (T const & t) { return t.b == b; };
std::pair<size_t, size_t> count1(std::vector<S> const & v) {
return {
std::count_if(v.begin(), v.end(), StructPred<true>{}),
std::count_if(v.begin(), v.end(), StructPred<false>{})
};
}
std::pair<size_t, size_t> count2(std::vector<S> const & v) {
return {
std::count_if(v.begin(), v.end(), make_pred<true>()),
std::count_if(v.begin(), v.end(), make_pred<false>())
};
}
std::pair<size_t, size_t> count3(std::vector<S> const & v) {
return {
std::count_if(v.begin(), v.end(), lambda_pred<true>),
std::count_if(v.begin(), v.end(), lambda_pred<false>)
};
}
std::pair<size_t, size_t> count4(std::vector<S> const & v) {
return {
std::count_if(v.begin(), v.end(), lambda_pred_t<S, true>),
std::count_if(v.begin(), v.end(), lambda_pred_t<S, false>)
};
}
void test() {
std::vector<S> v{3};
v[1].b = true;
// all implementations correctly return {1,2}
auto c1 = count1(v);
auto c2 = count2(v);
auto c3 = count3(v);
auto c4 = count4(v);
}