Code review and Lambdas

Hi all,

First, how do you find the code below, I mean if you review it, please?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "std_lib_facilities.h"
#include <numeric>
using namespace std;

template <typename C, typename P>
int count(const C& c, P pred)
{
	int cnt = 0;
	for (const auto& x : c)
		if (pred(x))
			++cnt;
	return cnt;
}

int main()
{
	int x = 5;
	string s = "test";

	vector<int> vi(10);
	iota(vi.begin(), vi.end(), 0);

	list<string> lst{ "tes", "tesy", "tesm", "test", "testa", "tesat", "tesr", "tesn", "testt", "tesc" };

	cout << "number of values less than " << x
		 << ": " << count(vi, [&](int a) { return a < x; }) << '\n';
	cout << "number of values less than " << s
		 << ": " << count(lst, [&](const string& a) { return a < s; }) << '\n';

	cin.get();
	return 0;
}



On line 26 and 28, the function count is called with vi and lst as the first argument, and the lambdas as the second argument.
My second questions is: the lambda part works as a predicate and a predicate is something we can invoke to return true or false. so the actual computations are done inside the function. Right?
Last edited on
I'm no expert on Lambda's, but your code looks pretty good to me. The only issue I see is using namespace std; You should avoid this, and instead prefix the standard library symbols with std:: or add using std::xyz for each symbol that you use.
Last edited on
The functionality of 'count' is already implemented in <algorithm> using the predicate in count_if.
https://en.cppreference.com/w/cpp/algorithm/count

. so the actual computations are done inside the function.
Not exactly sure what you're asking. count is a function, and you call it, and within count the pred function (which is the lambda you pass) is called once for each element in c.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <numeric>
using namespace std;

template <typename C, typename P>
void lessThan( const C& c, P x )
{
   cout << "Number of values less than " << x << ": " << count_if( begin( c ), end( c ), [x]( P e ){ return e < x; } ) << '\n';
}

int main()
{
	int x = 5;
	string s = "test";

	vector<int> vi(10);
	iota(vi.begin(), vi.end(), 0);

	list<string> lst{ "tes", "tesy", "tesm", "test", "testa", "tesat", "tesr", "tesn", "testt", "tesc" };

	lessThan( vi , x );
	lessThan( lst, s );
}
First, how do you find the code below, I mean if you review it, please?

So constructively teaching, as it's the author's bible:

3.4.3 Function Objects
One particularly useful kind of template is the function object (sometimes called a functor), which
is used to define objects that can be called like functions. For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
template<typename T>
class Less_than {
const T val;  //
value to compare against
public:
Less_than(const T& v) :val(v) { }
bool operator()(const T& x) const { return x<val; } //
call operator
};
The function called operator() implements the ‘‘function call,’’ ‘‘call,’’ or ‘‘application’’ operator ().
We can define named variables of type Less_than for some argument type:
Less_than<int> lti {42}; //
lti(i) will compare i to 42 using < (i<42)
Less_than<string> lts {"Backus"};  //
lts(s) will compare s to "Backus" using < (s<"Backus")
We can call such an object, just as we call a function:
void fct(int n, const string & s)
{
bool b1 = lti(n); //
true if n<42
bool b2 = lts(s); //
true if s<"Backus"
//
...
}

Such function objects are widely used as arguments to algorithms. For example, we can count the
occurrences of values for which a predicate returns true:
template<typename C, typename P>
int count(const C& c, P pred)
{
int cnt = 0;
for (const auto& x : c)
if (pred(x))
++cnt;
return cnt;
}
A predicate is something that we can invoke to return true or false. For example:
void f(const Vector<int>& vec, const list<string>& lst, int x, const string& s)
{
cout << "number of values less than " << x
<< ": " << count(vec,Less_than<int>{x})
<< '\n';
cout << "number of values less than " << s
<< ": " << count(lst,Less_than<string>{s})
<< '\n';
}


its used to specify the meaning of key operations of a general algorithm (such as Less_than for count()) are often referred to as policy objects.
We have to define Less_than separately from its use. That could be seen as inconvenient. Consequently, there is a notation for implicitly generating function objects
Such function objects are widely used as arguments to algorithms.
We can count the occurrences of values for which a predicate returns true:

1
2
3
4
5
6
7
8
9
void f(const Vector<int>& vec, const list<string>& lst, int x, const string& s)
{
cout << "number of values less than " << x
<< ": " << count(vec,[&](int a){ return a<x; })
<< '\n';
cout << "number of values less than " << s
<< ": " << count(lst,[&](const string& a){ return a<s; })
<< '\n';
}



The notation [&](int a){ return a<x; } is called a lambda expression (§11.4). It generates a function object exactly like Less_than<int>{x}.
The [&] is a capture list specifying that local names used
(such as x) will be passed by reference. Had we wanted to ‘‘capture’’ only x, we could have said so: [&x].
Had we wanted to give the generated object a copy of x, we could have said so: [x]. Capture nothing is [], capture all local names used by reference is [&], and capture all local names used by value is [=]... Ch. 3 p. 81
Last edited on
Thanks.

another question is: in
1
2
template <typename C, typename P>
int count(const C& c, P pred)

we have two types: C and P.

and in the function call: count(vi, [&](int a) { return a < x; }), vi has a type (vector of int) and goes for c, but the lambda hasn't a type to be recognized by p!

Will you explain this, please.
frek wrote:
the lambda hasn't a type to be recognized by p!

Please explain.
I mean, both typenames C and P are types, or defined to accept a type. The vector, as I mentioned, has a type, and the C accepts that type, and C will then be of type "vector<int>", but how about P? What will be its type when the lambda expression is sent to it (as the second argument)?

What will be its type when the lambda expression is sent to it (as the second argument)?
The [lambda] function is P in this case.
So when we define such templates as typename T, that T can not be necessarily a built-in or user-defined type, but a function, for instance. Right?
Last edited on
The C and P in template are placeholders that will be replaced with types when concrete function is instantiated from the template with actual types.

Lambda has a type.

std::count_if is a template: http://www.cplusplus.com/reference/algorithm/count_if/
1
2
3
4
5
6
template <class InputIterator, class UnaryPredicate>
  typename iterator_traits<InputIterator>::difference_type
    count_if (InputIterator first, InputIterator last, UnaryPredicate pred)
{
  // code
}

It is perfectly legal to call std::count_if with a lambda:
1
2
std::cout << std::count_if( vec.begin(), vec.end(),
    [&](int a){ return a<x; } );

When that is compiled, compiler first generates code for function from template by replacing InputIterator with type of vec and UnaryPredicate with type of [&](int a){ return a<x; }, and then compiles the code of function.


Do you actually ask: What is the type of lambda?
https://en.cppreference.com/w/cpp/language/lambda says:
The lambda expression is a prvalue expression of unique unnamed non-union non-aggregate class type, known as closure type
unique unnamed non-union non-aggregate class type
Yes, anything the compiler can actually deduce a type from can be used. lambdas do have a type as any other function too.
The lambda expression is a prvalue expression of unique unnamed non-union non-aggregate class type, known as closure type
unique unnamed non-union non-aggregate class type
Yes, I meant that, but it's a too complicated name!
Anyhow, the lambdas have types. what's the type of our lambda in my snippet, please?
Its parameter type is of int (int a). Its body contains int variables too. So we deduce the type of your lambda/function is int. Right?

Wrong.
lambdas have types
Some strictness is required when using terminology here to prevent ambiguity. Your use of saying a lambda "has" a type is like saying a function "has" a type. What does that actually refer to? Is it the return type? Well, no, it doesn't mean that.

A lambda itself is an object, and objects have types. The type that a lambda is, is as keskiverto says, some unique unnamed class type. You can't know what it is directly.

____________________

Its parameter type is of int (int a). Its body contains int variables too. So we deduce the type of your lambda/function is int. Right?
What would happen if the parameter type was double, and the return type was int? You couldn't just say the type of it was int. A lambda is not an int. A lambda is not a double. A lambda is a lambda, and each lambda is unique.

One more note: It's theoretically possible that lambdas could have been introduced into the language in a way akin to function pointers, where you can specify a combination of return type and parameter types, but a lambda is even more complicated than that (captures), and the language simply doesn't support that kind of syntax.
Last edited on
Topic archived. No new replies allowed.