일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- UE4
- c++
- 영화 리뷰
- 게임
- 참조자
- lua
- operator new
- 루아
- Effective c++
- 스마트 포인터
- Smart Pointer
- 다형성
- 비교 함수 객체
- more effective c++
- 함수 객체
- 오블완
- 암시적 변환
- 상속
- 메타테이블
- 티스토리챌린지
- exception
- reference
- 영화
- 반복자
- resource management class
- implicit conversion
- 언리얼
- 예외
- effective stl
- virtual function
Archives
- Today
- Total
스토리텔링 개발자
[Effective STL] 46. 함수 vs 함수 객체(람다) 본문
728x90
항목 46. 알고리즘의 매개 변수로는 함수 대신 함수 객체가 괜찮다
추상화(abstraction)와 성능
- 추상화가 높을수록 코드의 효율은 낮아진다.
- 그런데 함수 객체가 함수보다 빠르다니?
double의 벡터를 내림차순으로 정렬하기
vector<double> v;
...
// 방법 1
sort(v.begin(), v.end(), greater<double>()); // 함수 객체 greater를 사용했다.
// 방법 2
inline bool doubleGreater(double d1, double d2)
{
return d1 > d2;
}
sort(v.begin(), v.end(), doubleGreater); // 함수를 사용했다.
- 방법 1이 거의 확실히 더 빠르다.
- 이유는 인라인화(inlining)이다.
인라인화(inlining)
- 인라인으로 선언되어 있고, 함수의 몸체가 있다면
- 대부분의 컴파일러는 호출된 알고리즘의 템플릿 인스턴스화 과정에서 이를 인라인화 한다.
- greater<double>::operator()는 인라인화 하므로..
- sort가 인스턴스화 할 때 이 함수를 인라인 확장(inline-expand) 하게 된다.
- 즉, sort는 함수 호출을 전혀 하지 않는다.
- doubleGreater는 인라인화 되지 않는다.
- 함수는 복사로 동작할 수 없으므로, 함수 포인터로 넘겨진다.
- 보통의 컴파일러는 포인터 함수에 대해 인라인화를 하지 않는다.
- 그러므로 sort 내부에서 사용될 때마다 간접 함수 호출을 매번 하게 된다.
C++의 sort와 C의 qsort
- 재미있게도 함수 포인터 매개 변수가 인라인되지 않는다는 사실 때문에 qsort보다 sort가 속도 면에서 앞서게 된다.
함수 객체를 더 선호하는 또 다른 이유들
- STL 플랫폼에서 완벽하게 유효한 코드이지만 컴파일을 거부하는 상황이 발생할 수 있다.
set<string> s;
...
// 해당 STL 플랫폼이 const 멤버 함수(string::size)를 처리하는 데 버그를 가지고 있어서
// 컴파일 에러가 발생하는 상황이라 가정하면?
transfrom(s.begin(), s.end(), ostream_iterator<string::size_type>(cout, "\n"),
mem_fun_ref(&string::size));
// 아래처럼 함수 객체로 대처한다.
struct StringSize
{
string::size_type operator()(const string& s) const
{
return s.zie();
}
};
transfrom(s.begin(), s.end(), ostream_iterator<string::size_type>(cout, "\n"),
StringSize());
- 함수 객체가 미묘한 언어 문제를 막아줄 수 있다.
- 함수 템플릿의 인스턴스 이름이 함수 이름과 같지 않은 경우
// 두 부동소수점 실수의 평균 반환
template<typename FPType>
FPType average(FPType val1, FPType val2)
{
return (val1 + val2) / 2;
}
// 숫자열에서 두 개씩 짝을 맞춘 후 각각의 평균을 스트림에 기록
template<typename InputIter1, typename InputIter2>
void writeAverages(InputIter1 begin1, InputIter1 end1, InputIter2 begin2, ostream& s)
{
transform(begin1, end1, begin2,
ostream_iterator<typename iterator_traits<InputIter1>::value_type>(s, "\n"),
average<typename iterator_traits<InputIter1>::value_type> // 에러인가?
);
}
// 아래처럼 수정한다.
// 함수 객체 작성
template<typename FPType>
struct Average
{
FPType operator()(FPType val1, FPType val2) const
{
return average(val1, val2);
}
}
template<typename InputIter1, typename InputIter2>
void writeAverages(InputIter1 begin1, InputIter1 end1, InputIter2 begin2, ostream& s)
{
transform(begin1, end1, begin2,
ostream_iterator<typename iterator_traits<InputIter1>::value_type>(s, "\n"),
Average<typename iterator_traits<InputIter1>::value_type> // 함수 객체 사용
);
}
- 보통은 통과되지만, C++ 표준안에서는 금지하고 있다.
- 이론적으로 타입 매개 변수를 하나 가지는 average란 이름의 다른 함수 템플릿이 존재할 수 있다.
- 그러므로 average<typename iterator_traits<InputIter1>::value_type>은 애매한 표현식일 수 있다.
- 함수 객체로 수정하여 보완한다.
- 함수 객체로 수정한 위의 모든 사항은 사실 모던 C++에서는 람다로도 처리가 가능하다.
728x90
'개발 > Effective STL' 카테고리의 다른 글
[Effective STL] 48. include (0) | 2025.01.20 |
---|---|
[Effective STL] 47. 가독성이 떨어지는 코드 자제하기 (0) | 2025.01.17 |
[Effective STL] 45. 탐색 전략 선택 (0) | 2025.01.15 |
[Effective STL] 44. 같은 이름의 멤버 함수 vs 알고리즘 (0) | 2025.01.14 |
[Effective STL] 43. 루프 vs 알고리즘 (0) | 2025.01.13 |
Comments