일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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
- reference
- 게임
- Smart Pointer
- resource management class
- implicit conversion
- Vector
- Effective c++
- 루아
- lua
- 언리얼
- 참조자
- 영화
- exception
- 반복자
- 오블완
- 티스토리챌린지
- more effective c++
- UE4
- 예외
- 상속
- effective stl
- 스마트 포인터
- 비교 함수 객체
- 암시적 변환
- c++
- 다형성
- operator new
- 메타테이블
- 영화 리뷰
- virtual function
Archives
- Today
- Total
스토리텔링 개발자
[Effective STL] 39. predicate는 순수 함수일 것 본문
728x90
항목 39. 술어 구문은 순수 함수로 만들자
술어 구문(predicate)
- bool값을 반환하는 함수
- 판독의 기준이 되는 로직을 포함하는 구문이다.
순수 함수(pure function)
- 함수가 반환하는 값이 그 함수의 매개 변수에 종속되는 함수
- f(x, y)의 반환값은 x, y가 바뀔 때에만 변한다.
- 즉, 매개변수가 아닌 내부에 존재하는 데이터는 const여야 한다.
술어 구문 클래스(predicate class)
- operator()가 술어 구문인 함수 객체 클래스
술어 구문 클래스는 복사에 신경써야 한다.
- 함수 객체는 값으로 전달되기 때문이다.(항목 38 참조)
- 그리고 STL 알고리즘이 내부적으로 함수 객체의 사본을 따로 보관할 수도 있다.
- 그러므로 술어 구문 함수는 반드시 순수 함수여야 한다.
잘못 설계된 술어 구문 클래스
class BadPredicate
{
public:
BadPredicate() : timesCalled(0) {}
bool operator()(const Widget&)
{
return ++timesCalled == 3;
}
private:
size_t timesCalled; // 해당 값이 변경되며 순수 함수가 아니게 된다.
};
vector<Widget> vw;
...
// 3번째 요소만 삭제하고 싶다.
vw.erase(remove_if(vw.begin(), vw.end(), BadPredicate()), vw.end());
// 하지만 3번째 요소 뿐 아니라 6번째 요소도 삭제되어 버린다!
- 그 이유는 remove_if가 대체로 아래처럼 구현되기 때문이다.
template<typename FwdIterator, typename Predicate>
FwdIterator remove_if(FwdIterator begin, FwdIterator end, Predicate p)
{
// find_if : 주어진 범위 내에서 조건을 만족하는 첫 번째 요소 탐색
begin = find_if(begin, end, p); // timesCalled가 0인 p가 복사 전달된다.
if(begin == end)
{
return begin;
}
else
{
FwdIterator next = begin;
// remove_copy_if : 주어진 범위 내에서 조건을 만족하는 요소를 제외한 나머지 요소를 다른 범위로 복사
return remove_copy_if(++next, end, begin, p); // timesCalled가 0인 p가 다시 복사 전달된다.
}
}
술어 구문 함수를 순수 함수로 만드는 방법
- 술어 구문 클래스의 opeartor() 함수를 const 함수로 선언한다.
class BadPredicate
{
public:
bool operator()(const Widget&) const
{
return ++timesCalled == 3; // const 선언 되었으므로 에러가 발생한다!
}
...
};
- 하지만 이 방법만으로는, 아래 데이터에 접근하여 수정하는 것을 막을 수 없다.
- mutable로 선언된 멤버 데이터
- const가 아닌 지역 static 객체
- const가 아닌 클래스 static 객체
- 네임스페이스 스코프 안에 있는 const가 아닌 객체
- const가 아닌 전역 객체
- 술어 구문이 되는 모든 형태는 위 데이터에 접근하여 순수 함수가 되지 않을 여지가 있다.
bool anotherBadPrecdicate(const Widget&, const Widget&)
{
static int timesCalled = 0;
return ++timesCalled == 3; // 순수 함수가 아니다!!!!
}
- 람다의 경우 캡처를 사용하여 캡처까지 같이 복사되도록 하는 트릭을 사용할 수 있을 것이다.
- 이 경우 순수 함수가 아니지만 정상 동작한다.
int timesCalled = 0;
auto lambda = [×Called](const int&)
{
// timesCalled가 캡쳐되었다.
return ++timesCalled == 3;
};
vw.erase(remove_if(vw.begin(), vw.end(), lambda), vw.end()); // 정상 동작
728x90
'개발 > Effective STL' 카테고리의 다른 글
[Effective STL] 41. ptr_fun, mem_fun, mem_fun_ref(C++98) (0) | 2025.01.09 |
---|---|
[Effective STL] 40. adaptable한 함수 객체(C++98) (0) | 2025.01.08 |
[Effective STL] 38. 함수 객체 클래스 값 전달(pass by value) 고려하기 (0) | 2025.01.03 |
[Effective STL] 37. 범위 요약(summarize) (0) | 2025.01.02 |
[Effective STL] 36. copy_if (0) | 2024.12.26 |
Comments