C++ Primer learning notes - parameter binding bind

Problem elicitation

Using find_if, you can use a lambda expression as an argument to its third callable object. However, lambda expressions are only suitable for simple scenarios, such as expressions used only once or twice. When callable units need to be reused in large quantities, it is better to write them in function form. Therefore, we write check_ The size function is used to filter out strings with a size greater than sz.

bool check_size(const string& s, string::size_type sz)
{
	return s.size() >= sz;
}

int main()
{
	using std::placeholders::_1;

	vector<string> vec = { "hello", "y", "no", "1234567" };
	string::size_type sz = 2;
	
	auto it = find_if(vec.begin(), vec.end(), [sz](const string& s) -> bool {
		return s.size() >= sz;
	});
	cout << *it << endl;
	return 0;
}

The problem is, find_ The third parameter of if (callable object) will only pass one argument to the formal parameter, and the check defined by us_ Size requires 2 parameters. What should I do?
You can use the bind library function to solve this problem.

Standard library bind function

The bind library function can be regarded as defining a new function, which can bind the parameters required by the original function and modify the order of parameters (in the list) on the basis of the original function with fewer incoming parameters.
Header file

Its general calling form:

auto newCallable = bind(callable, arg_list);
/**
newCallable Is a callable object
arg_list It is a parameter separated parameter list corresponding to the given callable parameter
*/

When we call newCallable, newCallable will call callable and pass in arg_list as the callable parameter.

arg_ The list may contain shapes such as_ The name of n (n=1,2,3,...), as a "placeholder", represents the parameter of newCallable and occupies the "position" of the parameter passed to newCallable. For example_ 1 indicates the first parameter position of newCallable_ 2 indicates the second parameter position of newCallable.
be careful:
1) In bind_ 1, _ No sequence requirements for class 2;
2)_ 1, _ Placeholders such as 2 represent the parameters in the corresponding position of newCallable, not callable;

How to bind callable parameters?

As in the above example, how to bind check_ sz parameter of size?

#include <functional>

int sz = 6;
auto checksz = bind(check_size, _1, sz); 
string s = "hello"
bool b1 = checksz(s); // checksz(s) calls check_size(s, 6) 

auto it2 = find_if(vec.begin(), vec.end(), checksz); // find_if will pass a parameter to checksz_ 1 parameter, so attention should be paid_ The order of 1 and sz cannot be reversed
cout << *it2 << endl;

Use placeholders name

The previous program cannot be compiled and cannot recognize an identifier such as "_1", because it is located in the namespace named placeholders and needs to be declared with using before use.

For example, the using declaration of "_1"

using std::placeholders::_1;
...

Each placeholder is declared separately, which is cumbersome. We can use the using instruction to introduce all placeholders at one time:

using namespace std::placeholders;
...

Parameters of bind

Bind can be used to bind parameters for callable objects and rearrange the order,
For example, f is a callable object with 5 parameters, g is a generated new callable object with 2 parameters:

auto g = bind(f, a, b, _2, c, _1); // Calling g(_1, _2) calls f(a, b, _2, c, _1)

Reorder parameters with bind

Using the mapping from the new callable object to the original callable object, you can rearrange the order of parameters.
For example, in the above example,

auto g = bind(f, a, b, _2, c, _1); // Calling g(_1, _2) calls f(a, b, _2, c, _1)

auto g2 = bind(f, a, b, _1, c, _2); // Calling g(_1, _2) calls f(a, b, _1, c, _2)

Another example,

// Sort by word length from short to long
sort(words.begin(), words.end(), isShorter);
// Sort by word length from long to short
sort(words.begin(), words.end(), bind(isShorter, _2, _1));

When sort compares two elements A and B, isShorter(A, B) is called. After the bind reorders the parameters, it is equivalent to calling isShorter(B, A).

Binding reference parameters

By default, the parameters in bind that are not placeholders (_1, _2,...) are copied to the new callable object returned by bind (such as a,b,c in the above example), rather than passing values by reference. However, sometimes we want to pass parameters by reference. For example, copying the parameter object is prohibited or the copying cost is high. What should we do?
It is similar to value copy and reference copy in lambda expression. Similarly, you can pass the bind object reference to the standard library ref function as a parameter.

ostream& print(ostream &os, const string& s, char c) 
{
	return os << s << c;
}

// Error: unable to copy os
for_each(words.begin(), words.end(), bind(print, os, _1, ' ')); // Calling newprint(v) will call print(os, v, ''), and passing the value to os will cause copy

// OK: reference passing os
for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' ')); // Calling newprint(v) will call print(ref(os), v, ''). Passing the value to os will cause the reference copy, and the os reference is allowed to be copied

In addition, for const objects, you can use the cref function to pass references. Both ref and cref are located in

Summary

1) When the required callable object is incompatible with the existing function due to the number and order of parameters, and you do not want to redefine a function, you can consider using the bind library function to bind parameters and adjust the order of parameters for the existing function;
2) When the original function needs to pass parameters by reference, the ref and cref library functions can be used to pass object references for bind;

Tags: C++

Posted on Fri, 03 Dec 2021 05:26:35 -0500 by anthrt