C++ Primer CH10. Generic Algorithms
알고리즘 함수 종류는 100여개가 넘는다. 다 외우기 힘드니까 분류를 해보자
10.2.1 Read-Only Algorithms
10.2.2 Write Container Elements Algorithms
10.2.3 Reorder Container Algorithms
10.3 Customizing Operations
10.3.1 Passing a Function to an Algorithm
predicates
조건으로 활용될 수 있는 expression(표현식), 함수
10.3.2 Lambda Expressions
이름 없는 inline 함수라고 생각하면 된다.
Lambda 형태
[capture list](parameter list) -> return type { function body }
[sz](const string &a) {return a.size() >= sz; };
for_each Algorithms
auto wc = find_if(words.begin(), words.end(),
[sz](const string &a) { return a.size() >= sz; }); // lambda 함수 조건을 만족하는 첫번째 원소의 이터레이터 반환
auto count = words.end() - wc;
cout << count << " " << make_plural(count, "word", "s")
<< " of length " << sz << " or longer" << endl;
for_each(wc, words.end(), [](const string &s){cout << s << " ";});
// wc는 sz보다 크기 큰 첫번째 이터레이터 ~ end() 사이 원소에 대해 3번째 lambda 함수 수행
10.3.3 Lambda Captures and Returns
lambda는 인자로 들어갈 때 컴파일러가 알아서 이름 없는 클래스의 객체로 만들어준다.
lambda의 리턴값의 타입을 auto로 해주면 컴파일러가 알아서 자동생성된 클래스 타입으로 지정해준다.
Capture by Value
lambda의 capture 변수는 lambda 생성시 복사되어 lambda에 들어간다.
→ 함수 파라미터가 호출됐을 때 복사되는 것과는 대조된다.
void fnc1() {
size_t v1 = 42;
auto f = [v1]{return v1;};
v1 = 0;
auto j = f(); // j는 42이다. v1이 lambda에게 42로 복사되었고, 이걸 반환하기 때문
Capture by Reference
void fnc1() {
size_t v1 = 42;
auto f = [&v1]{return v1;};
v1 = 0;
auto j = f(); // j는 0이다. 캡처에 v1의 참조자가 들어갔기 때문. return v1은 바인딩된 객체의 값을 반환함
주의할 점
- 참조자를 캡처할 경우, 람다가 실행되는 동안 참조자가 유효해야 한다.
- 참조자나 포인터를 캡처 안하는게 좋다
Implicit Captures
람다의 바디를 보고 컴파일러가 알아서 어떤 변수를 캡처링해야하는지 추론하도록 하는 것
참조자 추론: [&] → 필요한 변수들을 모두 참조자로 불러옴
값 추론: [=] → 필요한 변수들을 모두 값으로 불러옴
implicit, explicit 섞어 쓸 수도 있다.
void biggies(vector<string> &words,
vector<string>::size_type sz,
ostream &os = cout, cahr c = ' ')
{
for_each(words.begin(), words.end(), [&, c, ..., (identifiers)](const string &s) { os << s << c; });
for_each(words.being(), words.end(), [=, &os, ..., (참조자들))(const string &s) }os << s << c; });
}
Mutable Lambdas
captured by value 들을 변경할 수 있게 하려면, mutable 키워드를 붙이면 된다.
Specifying the Lambda Return Type
lambda는 기본적으로 conditional operator의 타입 추론을 통해 반환 타입이 추론된다.
타입을 지정하는 법 → trailing return type
transform(vi.begin(), ve.end(), vi.begin(), [](int i) -> int
{ if (i < 0) return i; else return i; });
10.3.4 Binding Arguments
한정된 인자 개수의 함수가 있을 때, 추가 변수를 넘겨주고 싶다면 어떻게 해야 하는지? → bind function 사용
bind 함수의 정의된 위치: functional header
auto newCallable = bind(callable, arg_list);
// lambda
auto wc = find_if(words.begin(), words.end(),
[sz](const string &a)
// bind
auto wc = find_if(words.begin(), words.end(),
bind(check_size, _1, sz));
auto g = bind(f, a, b, _2, c, _1);
g(X,Y) == f(a, b, Y, c, X)
인자 순서를 바꿔주고 싶을 때
sort(words.begin(), words.end(), bind(isShorter, _2, _1));
bind는 기본적으로 copy를 통해 인자를 함수에 넘겨준다.
그런데, ostream같은 IO 객체는 copy가 불가능하다. 어떻게 해야할까?
ref 함수로 해당 객체를 감싸주면 된다.
#include <ostream>
#include <iostream>
#include <functional>
using namespace std;
using namespace std::placeholders;
ostream &print(ostream &os, const string &s, char c) {
return os << s << c;
}
int main(){
ostream& os = cout;
vector<string> words = {"hello", "bye", "world"};
for_each(words.begin(), words.end(), bind(print, ref(os), _1, ' '));
return 0;
}