programing tip

C ++에서 std :: function 또는 함수 포인터를 사용해야합니까?

itbloger 2020. 7. 6. 08:01
반응형

C ++에서 std :: function 또는 함수 포인터를 사용해야합니까?


C ++에서 콜백 함수를 구현할 때 여전히 C 스타일 함수 포인터를 사용해야합니까?

void (*callbackFunc)(int);

또는 std :: function을 사용해야합니까?

std::function< void(int) > callbackFunc;

요컨대,std::function 이유가없는 한 사용하십시오.

함수 포인터는 일부 컨텍스트 를 캡처 할 수 없다는 단점이 있습니다. 예를 들어 람다 함수를 일부 컨텍스트 변수를 캡처하는 콜백으로 전달할 수는 없지만 캡처하지 않으면 작동합니다. 따라서 객체 (- this포인터)를 캡처해야 하므로 객체의 멤버 변수를 호출하는 것도 불가능합니다 . (1)

std::function(C ++ 11부터)는 주로 함수 저장 하는 것입니다 (전달하면 저장하지 않아도 됨). 따라서 콜백을 멤버 변수에 저장하려면 아마도 최선의 선택 일 것입니다. 그러나 저장하지 않으면 호출 할 때 (매우 작은) 오버 헤드가 발생한다는 단점이 있지만 (성능이 중요한 상황에서는 문제가 될 수 있지만 대부분의 경우) 좋은 "첫 번째 선택"입니다. 안됩니다). 매우 "유니버설"입니다. 일관성 있고 읽을 수있는 코드에 대해 많은 관심을 갖고 있고, 선택하는 모든 선택에 대해 생각하고 싶지 않다면 (즉, 단순하게 유지하려는 경우) std::function전달하는 모든 함수에 사용하십시오 .

세 번째 옵션에 대해 생각 : 당신이 다음 제공된 콜백 함수를 통해 뭔가를보고 작은 기능을 구현하는 데 약이 있다면하는 생각 템플릿 매개 변수 다음이 될 수 있는 호출 객체 , 즉, 함수 포인터, 펑터, 람다, a std::function, ... 여기의 단점은 (외부) 함수가 템플릿이되므로 헤더에 구현해야한다는 것입니다. 반면에 (외부) 함수의 클라이언트 코드가 콜백에 대한 호출을 "인식"하면 사용 가능한 정확한 유형 정보가 표시되므로 콜백에 대한 호출이 인라인 될 수 있다는 이점이 있습니다.

템플리트 매개 변수가있는 버전의 예 ( C ++ 11 이전 &대신 쓰기 &&) :

template <typename CallbackFunction>
void myFunction(..., CallbackFunction && callback) {
    ...
    callback(...);
    ...
}

다음 표에서 볼 수 있듯이 모든 장단점이 있습니다.

+-------------------+--------------+---------------+----------------+
|                   | function ptr | std::function | template param |
+===================+==============+===============+================+
| can capture       |    no(1)     |      yes      |       yes      |
| context variables |              |               |                |
+-------------------+--------------+---------------+----------------+
| no call overhead  |     yes      |       no      |       yes      |
| (see comments)    |              |               |                |
+-------------------+--------------+---------------+----------------+
| can be inlined    |      no      |       no      |       yes      |
| (see comments)    |              |               |                |
+-------------------+--------------+---------------+----------------+
| can be stored     |     yes      |      yes      |      no(2)     |
| in class member   |              |               |                |
+-------------------+--------------+---------------+----------------+
| can be implemented|     yes      |      yes      |       no       |
| outside of header |              |               |                |
+-------------------+--------------+---------------+----------------+
| supported without |     yes      |     no(3)     |       yes      |
| C++11 standard    |              |               |                |
+-------------------+--------------+---------------+----------------+
| nicely readable   |      no      |      yes      |      (yes)     |
| (my opinion)      | (ugly type)  |               |                |
+-------------------+--------------+---------------+----------------+

(1)이 한계를 극복하기위한 대안이 있습니다 (예 : 추가 데이터를 추가 매개 변수로 (외부) 함수에 전달 : myFunction(..., callback, data)will call) callback(data). 이것이 C 스타일의 "인수를 이용한 콜백"인데, C ++에서 가능하며 (WIN32 API에서 많이 사용되는 방식으로) C ++에서 더 나은 옵션이 있으므로 피해야합니다.

(2) 클래스 템플릿에 대해 이야기하지 않는 한, 즉 함수를 저장하는 클래스는 템플릿입니다. 그러나 이것은 클라이언트 측에서 함수의 유형이 콜백을 저장하는 객체의 유형을 결정한다는 것을 의미합니다. 실제 사용 사례에는 거의 옵션이 아닙니다.

(3) C ++ 11 이전 버전의 경우 boost::function


void (*callbackFunc)(int); may be a C style callback function, but it is a horribly unusable one of poor design.

A well designed C style callback looks like void (*callbackFunc)(void*, int); -- it has a void* to allow the code that does the callback to maintain state beyond the function. Not doing this forces the caller to store state globally, which is impolite.

std::function< int(int) > ends up being slightly more expensive than int(*)(void*, int) invokation in most implementations. It is however harder for some compilers to inline. There are std::function clone implementations that rival function pointer invokation overheads (see 'fastest possible delegates' etc) that may make their way into libraries.

Now, clients of a callback system often need to set up resources and dispose of them when the callback is created and removed, and to be aware of the lifetime of the callback. void(*callback)(void*, int) does not provide this.

Sometimes this is available via code structure (the callback has limited lifetime) or through other mechanisms (unregister callbacks and the like).

std::function provides a means for limited lifetime management (the last copy of the object goes away when it is forgotten).

In general, I'd use a std::function unless performance concerns manifest. If they did, I'd first look for structural changes (instead of a per-pixel callback, how about generating a scanline processor based off of the lambda you pass me? which should be enough to reduce function-call overhead to trivial levels.). Then, if it persists, I'd write a delegate based off fastest possible delegates, and see if the performance problem goes away.

I would mostly only use function pointers for legacy APIs, or for creating C interfaces for communicating between different compilers generated code. I have also used them as internal implementation details when I am implementing jump tables, type erasure, etc: when I am both producing and consuming it, and am not exposing it externally for any client code to use, and function pointers do all I need.

Note that you can write wrappers that turn a std::function<int(int)> into a int(void*,int) style callback, assuming there are proper callback lifetime management infrastructure. So as a smoke test for any C-style callback lifetime management system, I'd make sure that wrapping a std::function works reasonably well.


Use std::function to store arbitrary callable objects. It allows the user to provide whatever context is needed for the callback; a plain function pointer does not.

If you do need to use plain function pointers for some reason (perhaps because you want a C-compatible API), then you should add a void * user_context argument so it's at least possible (albeit inconvenient) for it to access state that's not directly passed to the function.


The only reason to avoid std::function is support of legacy compilers that lack support for this template, which has been introduced in C++11.

If supporting pre-C++11 language is not a requirement, using std::function gives your callers more choice in implementing the callback, making it a better option compared to "plain" function pointers. It offers the users of your API more choice, while abstracting out the specifics of their implementation for your code that performs the callback.


std::function may bring VMT to the code in some cases, which has some impact on performance.

참고URL : https://stackoverflow.com/questions/25848690/should-i-use-stdfunction-or-a-function-pointer-in-c

반응형