내가 STL에 조예가 깊어서 글을 남기는 것이 아니라, Effecitve STL 을 공부하는 사람들이 이 글을 보고,
도움이 되었으면 하는 생각과, 혹시 내가 틀린것이 있다면 지적해 주시지 않을까 란 생각으로 글을 올리는것임을 미리 밝힙니다. - 최익필
이유는 3가지로 요약된다.
첫째, 함수의 포인터를 넘길때, 인라인화가 되지 않아 컴파일러 최적화가 되지 않는다. 이것은 CPU가 함수를 호출할 때의 원리를 알고 있어야 이해가 쉽게 될것이다. 인라인화와 그냥 함수 포인터를 이용한 호출은 CPU 명령 횟수의 차이를 보인다. 즉 함수 포인터로 함수 호출하는것은 그 만큼 많은 명령을 더 내려야 한다는 것이다.
둘째, 일반 함수를 이용할 경우 컴파일이 되지 않을 수 있다. STL 플랫폼이 const 멤버 함수(string::size)를 처리 하는데 버그를 가지고 있다. 이 문제를 해결할 방법은 새로운 함수 객체를 만드는 것이다. 이것은 인라인화도 갖어다 좋아 수행 성능도 좋아 진다.
셋째, 함수 객체가 미묘한 언어 문제를 막아 줄 수 있다. 이 부분은 이해가 잘 되지 않는다. 메타 프로그래밍 원리를 알아야지만 이해가 가능 할 듯으로 보인다. 아마도 템플릿 인스턴스화 할 때, 템플릿도 이름을 갖는데, 템플릿 함수의 경우 함수명을 가지고 템플릿 명을 갖는다. 이것은 여러 템플릿이 있을 경우, 같은 이름의 템플릿이 있을 가능성이 있고, 이것은 어떤 템플릿 으로 인스턴스화 하라는건지 컴파일러에게 정확하게 알려줄수 없다는 점을 지닌다.
이것은 함수 객체라는 객체로써 전달하게 된다면, 이 문제를 해결 할수 있다고 스콧 마이어스는 말해준다.
본 내용은 http://clay.yozmn.com/blogTag/ 객체 함수 에서 퍼온것임을 미리 밝힙니다. STL을 아는 사람은 Function Objects를 들어보았을 것이다. function objects가 무엇인지 외국 사이트의 내용을 번역해 올리고자 한다. 번역하는 데 나름대로 노력하겠지만과정중에 발생하는 오역에 대해서는 책임지지 않음을 밝혀둔다.
Defining a Function Object
Function Object 작성법 By Danny Kalev
Although pointers to functions are widely used for implementing function callbacks, C++ offers a significantly superior alternative to them, namely function objects. Function objects (also called "functors") are ordinary class objects that overload the () operator. Thus, syntactically, they behave like ordinary functions.
함 수 포인터가 함수callback을 구현하는데 널리 사용되지만, C++에서는 객체 함수(역자 주: function object는 함수 객체로 번역될 수 있으나 어감상 객체 함수가 더 나을 듯 하여 이 번역에서는 객체함수로 씀.)라는 월등히 우수한 대안을 제공해 주고 있다. Functor라고도 불리는 function object는 ()연산자 함수를 추가정의(역자 주: overloading: 재정의라고 할까 고민하다가 overrriding의 의미가 재정의에 맞는 것 같아 추가정의라고 번역하기로 함.)하는 보통의 클래스 객체이다.
There are several advantages in using a function object instead of a pointer to function. First, they are more resilient to design changes because the object can be modified internally without changing its external interface. A function object can also have data members that store the result of a previous call. When using ordinary functions, you need to store the result of a previous call in a global or a local static variable. However, global and local static variables have some undesirable characteristics. Finally, compilers can inline a call made through a function object, thereby enhancing performance even further. In contrast, it is nearly impossible to inline a function call made through a pointer.
함 수포인터 대신 객체 함수를 쓰는 데는 몇가지 좋은 점이 있다. 먼저, 객체함수는 외부 인터페이스를 변경하지 않은체 내부구현내용을 쉽게 바꿀 수 있기 때문에 디자인 변화에 좀더 융통성이 있다. 또한 객체 함수는 앞선 호출의 결과를 저장할 수 있는 데이터멤버를 가질 수 있다. 일반 함수의 경우, 전역 변수나 지역 정적 변수를 통해서 앞선 호출의 결과를 저장해야 한다. 이 방식의 문제점은 전역변수나 지역정적변수가 몇몇 원치않는 속성들을 가지고 있다는 것이다. 마지막으로, 컴파일러는 객체 함수를 통해 이루어진 호출을 inline화 할 수 있으며 이는 performance를 훨씬 증대시킬 수 있다. 반면 포인터를 통한 함수호출은 inline화 하는 것이 거의 불가능하다.
This solution will show how to define and use a function object that implements a negation operation. The first step consists of declaring an ordinary class and overloading the () operator:
아래 코드는 음수화작업을 구현하는 객체함수를 구현, 사용하는 방법을 보여주고 있다. 첫번째 단계는 보통의 클래스를 선언하고 ()연산자 함수를 추가정의하는 것으로 이루어 진다.
class Negate
{
public:
int operator() (int n) { return -n;}
};
The overloaded () might look a bit confusing because it has two pairs of parentheses. Remember that the first pair is always empty because it serves as the operator's name; the parameter list appears in the second pair of parentheses. Unlike other overloaded operators, whose number of parameters is fixed, the overloaded () operator may take any number of parameters.
Because the built-in negation operator is unary (it takes only a single operand), our overloaded () operator also takes a single parameter. The return type is identical to the parameter's type—int, in our example. The function body is trivial; it simply returns the negated argument.
추 가정의한 ()연산자 함수를 보면 괄호가 두번이나 쓰여 조금 이상해 보일지도 모른다. 첫번째 괄호는 항상 공백이라는 것을 기억해야 하는데 이 괄호는 연산자의 이름 역할을 한다; 함수의 매개변수 리스트는 두번째 괄호안에 나타나게 된다. 여타 연산자 추가정의 함수는 매개변수의 숫자가 고정되어 있지만, ()연산자 함수는 매개변수의 갯수에 제한이 없다. 원래 음수화함수가 하나의 매개변수만을 갖기 때문에 우리가 작성한 위 ()연산자함수도 역시 하나의 매개변수만을 갖고 있다. 리턴타입은 매개변수의 타입과 동일하며 위 예제에서는 int이다. 함수 내부는 간단하다; 그저 매개변수을 음수화하여 리턴할 뿐이다.
Using the Function Object
객체 함수 사용법
We now define a function named Callback() to test our function object. Callback() takes two arguments: an int and a reference to Negate. Callback() treats neg, the function object, as if it were a function's name:
이 제, 객체 함수를 테스트해보기 위해 Callback()이라는 함수를 정의해 보자. Callback()함수는 두개의 매개변수를 받는다, 정수와 Negate객체에 대한 참조자. Callback()함수는 neg라는 객체함수를 마치 함수이름처럼 취급한다.
#include "iostream" // 편의상 꺽쇠 괄호대신 "를 사용함
using std::cout;
void Callback(int n, Negate & neg)
{
int val = neg(n); //1번라인. 추가정의한 ()연산자함수를 호출
cout << val;
}
Don't let the syntax mislead you: neg is an object, not a function. The compiler transforms the line numbered 1 into the following:
문법에 속지 말자. neg는 객체이지 함수가 아니다. 컴파일러는 1번 라인을 다음과 같이 변형한다.
int val = neg.operator()(n);
In general, function objects do not define constructors and destructors. Therefore, they do not incur any overhead during their creation and destruction. As previously noted, the compiler can inline the overloaded operator's code, thereby avoiding the runtime overhead associated with a full-blown function call.
일 반적으로, 객체 함수는 생성자와 소멸자를 정의하지 않는다. 따라서, 생성, 소멸시에 부하가 걸리지 않는다. 앞선 언급했듯이, 컴파일러는 추가정의한 연산자의 코드를 inline화 할수 있으며 따라서 정상적인 함수 호출에서 발생하는 runtime부하를 피할 수 있다.
To complete the example, we need a main() driver to pass arguments to Callback():
예제를 완성하기 위해, 우리는 Callback()함수에 매개변수값을 건제룰 main()함수가 필요하다.
int main() { Callback(5, Negate() ); // -5 출력 }
The program passes the integer 5 and a temporary Negate object to Callback(). As expected, the program displays -5.
위 프로그램은 정수5와 임의의 Negate객체를 Callback()함수에 건네준다. 예상처럼 -5를 출력한다.
Template Function Objects Our example was confined to type int. However, one of the advantages of function objects is their generic nature. You can define the overloaded () operator as a member template so that it can work for any datatype: double, __int64 or char as follows: 위 예제는 int타입으로 한정되어 있지만 객체 함수의 좋은점중의 하나는 객체함수의 포괄적 속성이다.
class GenericNegate
{
public:
template T operator() (T t) const {return -t;}
};
int main()
{
GenericNegate negate;
cout<< negate(5.3333); // double
cout<< negate(10000000000i64); // __int64
}
Achieving this flexibility with ordinary callback functions is much more difficult.
이러한 융통서을 일반 콜백함수에서 구현하는 것은 엄청 어렵다.
Function Objects in the Standard Library
표준라이브러리에 포함된 객체 함수
The C++ Standard Library defines several useful function objects that can be plugged into STL algorithms. For example, the sort() algorithm takes a predicate object as its third argument. A predicate object is a templatized function object that returns a Boolean result. You can pass the predicates greater<> or less<> to sort() to force a descending or ascending sorting order, respectively:
C++ 표준라이브러리는 STL 알고리즘에 끼어넣을 수 있는 몇몇 유용한 객체함수를 정의해 놓고 있다. 예를 들어, sort()알고리즘은 세번째 변수로 predicate 객체를 받는다. predicate 객체는 템플릿 객체 함수로 boolean값을 리턴하는 객체를 말한다. greater<>나 less<>라는 predicate 객체를 sort()함수에 넣어 내림차순, 오름차순을 각각 적용할수 있다.
최근댓글