-
C++ Primer CH10. Generic Algorithmsc++ 2024. 1. 21. 16:26
알고리즘 함수 종류는 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; }
'c++' 카테고리의 다른 글
C++ Primer CH12. Dynamic Memory (0) 2024.01.21 C++ Primer CH11. Associative Containers (1) 2024.01.21 C++ Primer CH9. Sequential Containers (1) 2024.01.21 C++ Primer CH8. The IO Library (0) 2024.01.21 C++ Primer CH7. Classes (0) 2024.01.21