2
\$\begingroup\$

I created 3 different implementations (which produce the same results) using the count_if function from STL. I wish to check the number of elements in the vector which are strictly less than 6.

These are:

  1. C++ lambda function
  2. Global function (no functor)
  3. Using class and creating my own functor

These implementations work, but, how can I improve them into a more efficient and robust solutions?

#include <vector>
#include <algorithm>
#include <numeric>
#include <functional>
#include <iostream>

using namespace std;

// GLOBAL function (not FUNCTOR) See below for FUNCTOR implementation! 
template <int num>
bool lessThan(int x)
{
    return(x < num); // return TRUE if the value is less than NUM
}

// Paramterized Function with more robust FUNCTOR implementation (written by me) (I know stl has this built in)
class LessThan 
{
private:
    int m_x; // the value we wish to be less than 
public:
    LessThan(int val) : m_x(val) {} // custom constructor: assign a value to x
    bool operator() (int upper) // all values must be strictly less than the upper bound 
    {
        return (m_x < upper);
    }
};

int main()
{
    vector<int> vec = { 2, 7, 4, 5, 7, 7};

    // Using new C++ Lambda function implementation
    int x = count_if(vec.begin(), vec.end(), [](int x) {return x < 6; }); // using C++ Lambda Function (just a function with no name)
    cout <<"There exists " << x << " integers in vec which are strictly less than 6." << endl;

    // Using global lessThan6 implementation (NOT FUNCTOR)
    int y = count_if(vec.begin(), vec.end(), lessThan<6>); // not very robust 
    cout << "There exists " << y << " integers in vec which are strictly less than 6." << endl;

    // Using MY OWN FUNCTOR implementation 
    int z = count_if(vec.begin(), vec.end(), LessThan(6)); // robust 
    cout << "There exists " << z << " integers in vec which are strictly less than 6." << endl;

}
\$\endgroup\$
1
  • \$\begingroup\$ You don't say why you have 3 different implementations. Do you need 3? If not, then the one that uses the lambda expression is the best. The others are redundant. \$\endgroup\$
    – ksl
    Commented Jul 16, 2015 at 10:32

1 Answer 1

3
\$\begingroup\$

I'm not sure entirely what you're asking. If you're asking for which of three is "better" code between:

int x = count_if(vec.begin(), vec.end(), [](int x) {return x < 6; });
int y = count_if(vec.begin(), vec.end(), lessThan<6>);
int z = count_if(vec.begin(), vec.end(), LessThan(6));

I would say x then z then y. The reason for this is that the lambda solution is the only one where the actual functor is in-line with the algorithm. Anybody reading that code knows exactly what x should be immediately. There is no question. It is clear (to the extent that lambda syntax is) and concise.

z would be the C++03 way of doing this thing. It's... ok. We wrote that code for many years. The problem is the body of the functor is often hundreds of lines removed from the call, maybe in a different file... so readers of your code will have to find it. And it's never going to be perfectly clear either (who knows, maybe you named your functor weird and it's returning 6 < x? Naming things is really hard)

y is the worst. Having 6 as a template argument adds nothing to your performance (it's not like you're optimizing out a comparison), has equal readability loss compared to the lambda with the functor, and on top of that, can't even be called with a variable. With the other two, I can easily write:

int threshold = get_threshold();
int x = count_if(vec.begin(), vec.end(), [=](int x) {return x < threshold; });
int z = count_if(vec.begin(), vec.end(), LessThan(threshold));

But there's no way to pass threshold in as a template.

Typically, for algorithms, strongly prefer lambda unless (1) the algorithm call is a small part of the function and (2) the functor is very long and complicated. I say both must be met because something like this is still perfectly acceptable in my opinion:

int countBySomeCriteria() {
    return count_if(vec.begin(), vec.end(), [=](Foo const&) {
        /* some 15 line complicated functor */
    });
}

whereas if that call were surrounded by a lot of other logic, it would be much harder to easily make out.

Lambdas got added to the language basically to make stuff like this easier to write and easier to read. You should use them because they succeed in that goal.


PS. There is a fourth way to pass a functor: the lambda that was a lambda before C++11 lambdas: Boost.Lambda:

int q = count_if(vec.begin(), vec.end(), _1 < 6);

Make of that what you will.

\$\endgroup\$
5
  • 2
    \$\begingroup\$ Good review. One minor nit is that I'd be inclined to capture threshold explicitly by value [threshold](int x){return x < threshold; } rather than capturing all automatic variables as with [=]. See en.cppreference.com/w/cpp/language/lambda for more details on the options. \$\endgroup\$
    – Edward
    Commented Jul 16, 2015 at 11:23
  • 1
    \$\begingroup\$ so readers of your code will have to find it. Anybody using a decent editor (and we are all engineers so we are using good editors) should be one click away from the source. If I want to find it <ctrl> ] takes me directly to the source. \$\endgroup\$ Commented Jul 17, 2015 at 17:00
  • \$\begingroup\$ Otherwise totally agree: Prefer Lambda as long as it is short. Short is a relative term and will depend on context. \$\endgroup\$ Commented Jul 17, 2015 at 17:05
  • \$\begingroup\$ @Barry: You are implying that it is hard to find and inconvenient (true if you are using notepad but otherwise not). <ctrl> ]:sp<return>:pop<return> Takes me less than a second to get the code sitting side by side with the call point. \$\endgroup\$ Commented Jul 17, 2015 at 17:19
  • \$\begingroup\$ @Barry: You spent a whole paragraph on the topic to explain as a reason to explain why lambdas are better. So if finding the code nor comparing it against the original is not a hindrance there is no need to mention it. \$\endgroup\$ Commented Jul 17, 2015 at 17:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.