[C++ Primer Plus] Chapter 18. (2) 람다 함수, 래퍼, 가변인자 템플릿
Table of Contents
C++ 기초 플러스 책을 읽고 공부한 노트입니다.
람다 함수 #
- 랜덤한 정수들 중에서 얼마나 많은 수가 3으로, 13으로 나뉘는지 확인하고 싶다.
1. 함수 포인터
bool F3(int x) { return (x % 3) == 0; }
bool F13(int x) { return (x % 13) == 0; }
int main()
{
vector<int> numbers(1000);
generate(numbers.begin(), numbers.end(), rand);
int count3 = count_if(numbers.begin(), numbers.end(), F3);
int count13 = count_if(numbers.begin(), numbers.end(), F13);
}
2. 펑크터
class FMod
{
private:
int dv;
public:
FMod(int d = 1) : dv(d) {}
bool operator()(int x) { return (x % dv) == 0; }
};
int main()
{
vector<int> numbers(1000);
generate(numbers.begin(), numbers.end(), rand);
int count3 = count_if(numbers.begin(), numbers.end(), FMod(3));
int count13 = count_if(numbers.begin(), numbers.end(), FMod(13));
}
3. 람다
- 함수 이름이
[]
이 되었다. - 반환 타입은 선언되지 않았다.
decltype
이 반환 값으로부터 추정된 타입이 반환 타입이 된다.- 만약, 단일 구문을 반환하는 구조가 아니라서 반환 타입이 자동으로 결정되지 않는다면 반환 값을 추정하는 문법이 필요하다.
int main()
{
vector<int> numbers(1000);
generate(numbers.begin(), numbers.end(), rand);
int count3 = count_if(numbers.begin(), numbers.end(), [](int x){ return (x % 3) == 0; });
int count13 = count_if(numbers.begin(), numbers.end(), [](int x){ return (x % 13) == 0; });
}
[](int i)->double { double d = i; return d - i; }
// 반환 타입은 double형이다.
- 왜 람다를 쓰는가?
- 근접성
- 사용하는 곳 가까이에 정의하는 것이 유용하다.
count_if()
함수 세 번째 매개변수가 호출하는 것이 무엇인지 코드를 스캔하고 싶지 않다.
- 사용하는 곳 가까이에 정의하는 것이 유용하다.
- 간결함
- 람다에 이름을 생성하여 반복해 사용할 수 있다.
auto mod3 = [](int x){ return (x % 3) == 0; };
- 효율
- 인라인화가 되어 접근이 빠르다.
- 능력
- 범위 내에서 모든 자동화된 변수 이름으로 접근이 가능하다.
[변수이름]
으로 변수를 값으로 접근한다.[&변수이름]
으로 변수를 참조로 접근한다.[&]
으로 모든 자동화된 변수를 참조로 접근한다.[=]
으로 모든 자동화된 변수를 값으로 접근한다.- 혼합하여 사용하는 것도 가능하다.
- 범위 내에서 모든 자동화된 변수 이름으로 접근이 가능하다.
- 근접성
int count3 = 0;
for_each(numbers.begin(), numbers.end(), [&count3](int x){ count3 += ((x % 3) == 0); });
int count3 = count13 = 0;
for_each(numbers.begin(), numbers.end(),
[&](int x){ count3 += ((x % 3) == 0); count13 += ((x % 13) == 0); });
래퍼(Wrapper) #
- 템플릿의 비효율성
- 많은 타입이 존재할 수 있는 가능성은 템플릿의 비효율성을 초래할 수 있다.
- 아래 예제와 같은 경우
F
의 시그니처는double (double)
로 동일함에도 불구하고,UseFunction()
의 인스턴스가 5개나 생성되었다.
template <typename T, typename F>
T UseFunction(T v, F f)
{
static int count = 0; // static 변수로 얼마나 많은 인스턴스가 생성되는지 체크한다.
count++;
cout << "UseFunction count = " << count << ", &count = " << &count << endl;
return f(v);
}
class Fp
{
private:
double z;
public:
Fp(double zz = 1.0) : z(zz) {}
double operator()(double p) { return z * p; }
};
class Fq
{
private:
double z;
public:
Fq(double zz = 1.0) : z(zz) {}
double operator()(double q) { return z + q; }
};
double Double(double x)
{
return 2.0 * x;
}
double Square(double x)
{
return x * x;
}
int main()
{
double y = 1.21;
cout << "함수 포인터 Double:\n"; // double (*) (double)
cout << " " << UseFunction(y, Double) << endl << endl;
cout << "함수 포인터 Square:\n"; // double (*) (double)로 같다.
cout << " " << UseFunction(y, Square) << endl << endl;
cout << "펑크터 Fp:\n"; // Fp의 인스턴스
cout << " " << UseFunction(y, Fp(5.0)) << endl << endl;
cout << "펑크터 Fq:\n"; // Fq의 인스턴스
cout << " " << UseFunction(y, Fq(5.0)) << endl << endl;
cout << "람다 표현식 1:\n";
cout << " " << UseFunction(y, [](double u){return u * u; }) << endl << endl;
cout << "람다 표현식 2:\n";
cout << " " << UseFunction(y, [](double u){return u + u / 2.0; }) << endl << endl;
}
함수 포인터 Double:
UseFunction count = 1, &count = 001E03D8
2.42
함수 포인터 Square:
UseFunction count = 2, &count = 001E03D8
1.4641
펑크터 Fp:
UseFunction count = 1, &count = 001E03DC
6.05
펑크터 Fq:
UseFunction count = 1, &count = 001E03E0
6.21
람다 표현식 1:
UseFunction count = 1, &count = 001E03E4
1.4641
람다 표현식 2:
UseFunction count = 1, &count = 001E03E8
1.815
- 함수 래퍼로 문제 해결
function<>
템플릿은 같은 함수 시그내처를 갖는 함수 포인터, 펑크터, 람다 표현식을 포장하는데 사용한다.- 아래 코드와 같은 경우,
function<double(double)>
을 사용하여 여섯 개의 래퍼를 생성한다. - 이것은
F
를 모두 같은 타입으로 만든다. 따라서 최종적으로 1개의UseFunction()
인스턴스만 만들어진다.
double y = 1.21;
// 6개의 래퍼를 생성한다.
function<double(double)> ef1 = Double;
function<double(double)> ef2 = Square;
function<double(double)> ef3 = Fq(5.0);
function<double(double)> ef4 = Fp(5.0);
function<double(double)> ef5 = [](double u){ return u * u; };
function<double(double)> ef6 = [](double u){ return u + u / 2.0; };
cout << "함수 포인터 Double:\n";
cout << " " << UseFunction(y, ef1) << endl << endl;
cout << "함수 포인터 Square:\n";
cout << " " << UseFunction(y, ef2) << endl << endl;
cout << "펑크터 Fp:\n";
cout << " " << UseFunction(y, ef3) << endl << endl;
cout << "펑크터 Fq:\n";
cout << " " << UseFunction(y, ef4) << endl << endl;
cout << "람다 표현식 1:\n";
cout << " " << UseFunction(y, ef5) << endl << endl;
cout << "람다 표현식 2:\n";
cout << " " << UseFunction(y, ef6) << endl << endl;
함수 포인터 Double:
UseFunction count = 1, &count = 00945660
2.42
함수 포인터 Square:
UseFunction count = 2, &count = 00945660
1.4641
펑크터 Fp:
UseFunction count = 3, &count = 00945660
6.21
펑크터 Fq:
UseFunction count = 4, &count = 00945660
6.05
람다 표현식 1:
UseFunction count = 5, &count = 00945660
1.4641
람다 표현식 2:
UseFunction count = 6, &count = 00945660
1.815
typedef
로 간소화한 표현이 가능하다.
double y = 1.21;
typedef function<double(double)> fdd;
//...
cout << " " << UseFunction(y, fdd(Double)) << endl << endl;
- 템플릿 자체를 바꾸어 볼 수도 있다.
template <typename T>
T UseFunction(T v, function<T(T)> f)
{
static int count = 0;
count++;
cout << "UseFunction count = " << count << ", &count = " << &count << endl;
return f(v);
}
가변인자 템플릿 #
- 임의의 개수의 인자를 받는 함수를 만들 수는 없을까?
- 템플릿 매개변수 팩(Template parameter packs), 함수 매개변수 팩(Function parameter packs)
// ...는 0개 이상의 인자들을 나타낸다.
template<typename... Args> // 템플릿 매개변수 팩
void Show(Args... args) // 함수 매개변수 팩
{
}
int main()
{
Show();
Show("hello");
Show(4.5, 1, string("str"), 'c');
}
- 언패킹 팩(Unpacking a pack)
- 어떻게 팩의 내용을 언패킹할 수 있을까?
- 즉,
Show()
함수 내에서 어떻게 팩의 내용인4.5
,1
,string("str")
,c
들을 접근할 수 있을까?
- 재귀(Recursion)
- 재귀를 적절하게 사용하면 팩의 아이템들에 접근할 수 있다.
template <typename T>
void Show(T arg) // 마지막 매개변수에 대한 처리.
{
cout << arg << endl;
}
template <typename T, typename... Args>
void Show(T arg, Args... args)
{
cout << arg << ", ";
Show(args...); // 첫번째 매개변수를 제외한 매개변수로 재귀적으로 호출한다.
}
int main()
{
int n = 14;
double x = 2.71828;
string mr = "Mr. String objects!";
Show(n, x);
Show(x * x, '!', 7, mr);
}
14, 2.71828
7.38905, !, 7, Mr. String objects!