c++

C++ Primer CH10. Generic Algorithms

big whale 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;
}