C ++의 콜백 함수 C ++에서 언제

C ++에서 언제 어떻게 콜백 함수를 사용합니까?

편집 :
콜백 함수를 작성하는 간단한 예를보고 싶습니다.



답변

참고 : 대부분의 답변은 함수 포인터를 다루며 C ++에서 “콜백”논리를 달성 할 수있는 가능성은 있지만 오늘날 가장 좋은 것은 아닙니다.

콜백 (?)이란 무엇이며 왜 사용해야합니까 (!)

콜백은 클래스 또는 함수에 의해 허용되는 콜 러블 (추가 참조)이며 해당 콜백에 따라 현재 로직을 사용자 정의하는 데 사용됩니다.

콜백을 사용하는 한 가지 이유 는 호출 된 함수의 논리와 독립적이며 다른 콜백과 함께 재사용 할 수있는 일반 코드 를 작성 하는 것입니다.

표준 알고리즘 라이브러리의 많은 함수는 <algorithm>콜백을 사용합니다. 예를 들어, for_each알고리즘은 반복자 범위의 모든 항목에 단항 콜백을 적용합니다.

template<class InputIt, class UnaryFunction>
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f)
{
  for (; first != last; ++first) {
    f(*first);
  }
  return f;
}

예를 들어 적절한 콜 러블을 전달하여 벡터를 먼저 증가시킨 다음 인쇄하는 데 사용할 수 있습니다.

std::vector<double> v{ 1.0, 2.2, 4.0, 5.5, 7.2 };
double r = 4.0;
std::for_each(v.begin(), v.end(), [&](double & v) { v += r; });
std::for_each(v.begin(), v.end(), [](double v) { std::cout << v << " "; });

인쇄

5 6.2 8 9.5 11.2

콜백의 또 다른 적용은 일정량의 정적 / 컴파일 시간 유연성을 가능하게하는 특정 이벤트를 호출자에게 알리는 것입니다.

개인적으로 두 가지 콜백을 사용하는 로컬 최적화 라이브러리를 사용합니다.

  • 함수 값과 입력 값 벡터를 기반으로하는 그래디언트가 필요한 경우 첫 번째 콜백이 호출됩니다 (논리 콜백 : 함수 값 결정 / 그라디언트 유도).
  • 두 번째 콜백은 각 알고리즘 단계마다 한 번씩 호출되며 알고리즘 수렴에 대한 특정 정보 (알림 콜백)를 수신합니다.

따라서 라이브러리 디자이너는 알림 콜백을 통해 프로그래머에게 제공되는 정보로 발생하는 정보를 결정할 책임이 없으며 로직 콜백에서 제공되므로 함수 값을 실제로 결정하는 방법에 대해 걱정할 필요가 없습니다. 이러한 것들을 올바르게 얻는 것은 라이브러리 사용자로 인한 작업이며 라이브러리를 얇고 일반적으로 유지합니다.

또한 콜백은 동적 런타임 동작을 가능하게합니다.

사용자가 키보드의 버튼과 게임 동작을 제어하는 ​​일련의 기능을 누를 때마다 실행되는 기능이있는 일종의 게임 엔진 클래스를 상상해보십시오. 콜백을 사용하면 런타임에 수행 할 작업을 (다시) 결정할 수 있습니다.

void player_jump();
void player_crouch();

class game_core
{
    std::array<void(*)(), total_num_keys> actions;
    // 
    void key_pressed(unsigned key_id)
    {
        if(actions[key_id]) actions[key_id]();
    }
    // update keybind from menu
    void update_keybind(unsigned key_id, void(*new_action)())
    {
        actions[key_id] = new_action;
    }
};

여기서 함수 key_pressed는 저장된 actions키를 사용하여 특정 키를 누를 때 원하는 동작을 얻습니다. 플레이어가 점프 버튼을 변경하기로 선택하면 엔진은

game_core_instance.update_keybind(newly_selected_key, &player_jump);

따라서 게임에서 다음에이 버튼을 누르면 호출 동작을 key_pressed(통화 player_jump)로 변경합니다 .

C ++ (11)에서 호출 가능한 것은 무엇입니까 ?

보다 공식적인 설명 은 C ++ 개념 : cppreference에서 호출 가능 을 참조하십시오 .

콜백 기능은 C ++ (11)에서 여러 가지 방법으로 실현 될 수 있습니다. 여러 가지 다른 것들이 호출 가능 하다는 것이 밝혀졌습니다 .

  • 함수 포인터 (멤버 ​​함수에 대한 포인터 포함)
  • std::function 사물
  • 람다 식
  • 바인드 표현식
  • 함수 객체 (오버로드 된 함수 호출 연산자가있는 클래스 operator())

* 참고 : 데이터 멤버에 대한 포인터도 호출 가능하지만 함수는 전혀 호출되지 않습니다.

콜백 을 자세하게 작성하는 몇 가지 중요한 방법

  • X.1이 포스트에서 콜백을 “쓰기”는 콜백 유형을 선언하고 이름을 지정하는 구문을 의미합니다.
  • X.2 “콜링”콜백은 해당 객체를 호출하는 구문을 나타냅니다.
  • X.3 콜백 “사용”은 콜백을 사용하여 인수를 함수에 전달할 때 구문을 의미합니다.

참고 : C ++ 17 부터 멤버 케이스에 대한 포인터를 처리하는 f(...)것과 같은 호출을 작성할 수 있습니다 std::invoke(f, ...).

1. 함수 포인터

함수 포인터는 콜백이 가질 수있는 ‘가장 단순하다'(일반성, 가독성은 최악)의 유형입니다.

간단한 기능을 봅시다 foo:

int foo (int x) { return 2+x; }

1.1 함수 포인터 / 타입 표기법 작성

함수 포인터 타입은 표기법이있다

return_type (*)(parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a pointer to foo has the type:
int (*)(int)

어디 라는 이름의 함수 포인터 타입이 모양을

return_type (* name) (parameter_type_1, parameter_type_2, parameter_type_3)

// i.e. f_int_t is a type: function pointer taking one int argument, returning int
typedef int (*f_int_t) (int);

// foo_p is a pointer to function taking int returning int
// initialized by pointer to function foo taking int returning int
int (* foo_p)(int) = &foo;
// can alternatively be written as 
f_int_t foo_p = &foo;

using선언은 typedeffor f_int_t를 다음과 같이 작성할 수 있기 때문에 조금 더 읽기 쉬운 옵션을 제공합니다 .

using f_int_t = int(*)(int);

(적어도 나에게는) f_int_t새로운 유형 별칭이며 함수 포인터 유형의 인식이 더 쉬운 곳이 더 명확합니다.

그리고 함수 포인터 타입의 콜백을 사용한 함수 선언 은 다음과 같습니다.

// foobar having a callback argument named moo of type 
// pointer to function returning int taking int as its argument
int foobar (int x, int (*moo)(int));
// if f_int is the function pointer typedef from above we can also write foobar as:
int foobar (int x, f_int_t moo);

1.2 콜백 통화 표기

호출 표기법은 간단한 함수 호출 구문을 따릅니다.

int foobar (int x, int (*moo)(int))
{
    return x + moo(x); // function pointer moo called using argument x
}
// analog
int foobar (int x, f_int_t moo)
{
    return x + moo(x); // function pointer moo called using argument x
}

1.3 콜백 사용 표기법 및 호환 가능한 유형

함수 포인터를 사용하여 함수 포인터를 사용하는 콜백 함수를 호출 할 수 있습니다.

함수 포인터 콜백을받는 함수를 사용하는 것은 다소 간단합니다.

 int a = 5;
 int b = foobar(a, foo); // call foobar with pointer to foo as callback
 // can also be
 int b = foobar(a, &foo); // call foobar with pointer to foo as callback

1.4 예

콜백 작동 방식에 의존하지 않는 함수를 작성해야합니다.

void tranform_every_int(int * v, unsigned n, int (*fp)(int))
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

콜백이 가능한 곳

int double_int(int x) { return 2*x; }
int square_int(int x) { return x*x; }

처럼 사용

int a[5] = {1, 2, 3, 4, 5};
tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};
tranform_every_int(&a[0], 5, square_int);
// now a == {4, 16, 36, 64, 100};

2. 멤버 함수의 포인터

멤버 함수에 대한 포인터 (일부 클래스의 C)는 특수 유형의 (그리고 훨씬 더 복잡한) 함수 포인터로, 유형의 객체 C가 작동해야합니다.

struct C
{
    int y;
    int foo(int x) const { return x+y; }
};

2.1 멤버 함수 / 타입 표기법에 대한 포인터 쓰기

멤버 함수 유형에 대한 포인터 일부 클래스는 T표기법이있다

// can have more or less parameters
return_type (T::*)(parameter_type_1, parameter_type_2, parameter_type_3)
// i.e. a pointer to C::foo has the type
int (C::*) (int)

멤버 함수에 대한 명명 된 포인터 는 함수 포인터와 유사하게 다음과 같습니다.

return_type (T::* name) (parameter_type_1, parameter_type_2, parameter_type_3)

// i.e. a type `f_C_int` representing a pointer to member function of `C`
// taking int returning int is:
typedef int (C::* f_C_int_t) (int x);

// The type of C_foo_p is a pointer to member function of C taking int returning int
// Its value is initialized by a pointer to foo of C
int (C::* C_foo_p)(int) = &C::foo;
// which can also be written using the typedef:
f_C_int_t C_foo_p = &C::foo;

예 : 멤버 함수 콜백에 대한 포인터를 인수 중 하나로 사용 하는 함수 선언 :

// C_foobar having an argument named moo of type pointer to member function of C
// where the callback returns int taking int as its argument
// also needs an object of type c
int C_foobar (int x, C const &c, int (C::*moo)(int));
// can equivalently declared using the typedef above:
int C_foobar (int x, C const &c, f_C_int_t moo);

2.2 콜백 통화 표기

역 참조 된 포인터에 대한 멤버 액세스 작업을 사용하여 C유형의 객체와 관련 하여 멤버에 대한 포인터 함수를 호출 할 수 있습니다 C.
참고 : 괄호가 필요합니다!

int C_foobar (int x, C const &c, int (C::*moo)(int))
{
    return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}
// analog
int C_foobar (int x, C const &c, f_C_int_t moo)
{
    return x + (c.*moo)(x); // function pointer moo called for object c using argument x
}

참고 :에 대한 포인터를 C사용할 수있는 경우 구문은 동일합니다 (포인터 C도 역 참조해야 함).

int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
    if (!c) return x;
    // function pointer meow called for object *c using argument x
    return x + ((*c).*meow)(x);
}
// or equivalent:
int C_foobar_2 (int x, C const * c, int (C::*meow)(int))
{
    if (!c) return x;
    // function pointer meow called for object *c using argument x
    return x + (c->*meow)(x);
}

2.3 콜백 사용 표기법 및 호환 가능한 유형

클래스 T의 멤버 함수 포인터를 사용하는 콜백 함수는 class 의 멤버 함수 포인터를 사용하여 호출 할 수 있습니다 T.

멤버 함수 콜백에 대한 포인터를 취하는 함수를 사용하는 것은 함수 포인터와 유사합니다.

 C my_c{2}; // aggregate initialization
 int a = 5;
 int b = C_foobar(a, my_c, &C::foo); // call C_foobar with pointer to foo as its callback

3. std::function객체 (헤더 <functional>)

std::function클래스 저장, 복사 또는 callables 호출 다형성 함수 래퍼이다.

3.1 std::function객체 / 타입 표기법 작성

std::function콜 러블을 저장 하는 객체 의 유형은 다음과 같습니다.

std::function<return_type(parameter_type_1, parameter_type_2, parameter_type_3)>

// i.e. using the above function declaration of foo:
std::function<int(int)> stdf_foo = &foo;
// or C::foo:
std::function<int(const C&, int)> stdf_C_foo = &C::foo;

3.2 콜백 통화 표기

클래스 std::functionoperator()대상을 호출하는 데 사용할 수있는 클래스 를 정의했습니다.

int stdf_foobar (int x, std::function<int(int)> moo)
{
    return x + moo(x); // std::function moo called
}
// or 
int stdf_C_foobar (int x, C const &c, std::function<int(C const &, int)> moo)
{
    return x + moo(c, x); // std::function moo called using c and x
}

3.3 콜백 사용 표기법 및 호환 가능한 유형

std::function다른 유형 통과 암시 적으로 변환 할 수 있기 때문에 콜백 함수 포인터 또는 멤버 함수 포인터보다 더 일반적이기 std::function객체입니다.

3.3.1 함수 포인터와 멤버 함수에 대한 포인터

함수 포인터

int a = 2;
int b = stdf_foobar(a, &foo);
// b == 6 ( 2 + (2+2) )

또는 멤버 함수에 대한 포인터

int a = 2;
C my_c{7}; // aggregate initialization
int b = stdf_C_foobar(a, c, &C::foo);
// b == 11 == ( 2 + (7+2) )

사용할 수 있습니다.

3.3.2 람다 식

람다 식에서 명명되지 않은 클로저를 std::function객체에 저장할 수 있습니다 .

int a = 2;
int c = 3;
int b = stdf_foobar(a, [c](int x) -> int { return 7+c*x; });
// b == 15 ==  a + (7*c*a) == 2 + (7+3*2)

3.3.3 std::bind표현

std::bind식 의 결과를 전달할 수 있습니다. 예를 들어 매개 변수를 함수 포인터 호출에 바인딩하면

int foo_2 (int x, int y) { return 9*x + y; }
using std::placeholders::_1;

int a = 2;
int b = stdf_foobar(a, std::bind(foo_2, _1, 3));
// b == 23 == 2 + ( 9*2 + 3 )
int c = stdf_foobar(a, std::bind(foo_2, 5, _1));
// c == 49 == 2 + ( 9*5 + 2 )

멤버 함수에 대한 포인터 호출을위한 객체로 객체를 바인딩 할 수도 있습니다.

int a = 2;
C const my_c{7}; // aggregate initialization
int b = stdf_foobar(a, std::bind(&C::foo, my_c, _1));
// b == 1 == 2 + ( 2 + 7 )

3.3.4 함수 객체

operator()오버로드가 적절한 클래스 의 std::function객체도 객체 내부에 저장할 수 있습니다 .

struct Meow
{
  int y = 0;
  Meow(int y_) : y(y_) {}
  int operator()(int x) { return y * x; }
};
int a = 11;
int b = stdf_foobar(a, Meow{8});
// b == 99 == 11 + ( 8 * 11 )

3.4 예

사용할 함수 포인터 예제 변경 std::function

void stdf_tranform_every_int(int * v, unsigned n, std::function<int(int)> fp)
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

(3.3 참조) 더 많은 기능을 사용할 수 있기 때문에 그 기능에 훨씬 더 많은 유용성을 제공합니다.

// using function pointer still possible
int a[5] = {1, 2, 3, 4, 5};
stdf_tranform_every_int(&a[0], 5, double_int);
// now a == {2, 4, 6, 8, 10};

// use it without having to write another function by using a lambda
stdf_tranform_every_int(&a[0], 5, [](int x) -> int { return x/2; });
// now a == {1, 2, 3, 4, 5}; again

// use std::bind :
int nine_x_and_y (int x, int y) { return 9*x + y; }
using std::placeholders::_1;
// calls nine_x_and_y for every int in a with y being 4 every time
stdf_tranform_every_int(&a[0], 5, std::bind(nine_x_and_y, _1, 4));
// now a == {13, 22, 31, 40, 49};

4. 템플릿 콜백 유형

템플릿을 사용하면 콜백을 호출하는 코드가 std::function객체를 사용하는 것보다 훨씬 일반적 일 수 있습니다 .

템플릿은 컴파일 타임 기능이며 컴파일 타임 다형성을위한 디자인 도구입니다. 콜백을 통해 런타임 동적 동작을 달성하려면 템플릿이 도움이되지만 런타임 동적을 유도하지는 않습니다.

4.1 쓰기 (타입 표기법) 및 템플릿 콜백 호출

std_ftransform_every_int템플릿을 사용하면 위에서부터 코드를 일반화 할 수 있습니다.

template<class R, class T>
void stdf_transform_every_int_templ(int * v,
  unsigned const n, std::function<R(T)> fp)
{
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = fp(v[i]);
  }
}

콜백 유형에 대한 훨씬 일반적인 (그리고 가장 쉬운) 구문을 사용하여 일반적인 템플릿 템플릿 인수로 사용하십시오.

template<class F>
void transform_every_int_templ(int * v,
  unsigned const n, F f)
{
  std::cout << "transform_every_int_templ<"
    << type_name<F>() << ">\n";
  for (unsigned i = 0; i < n; ++i)
  {
    v[i] = f(v[i]);
  }
}

참고 : 포함 된 출력은 템플릿 유형에 대해 추론 된 유형 이름을 인쇄합니다 F. 구현은 type_name이 게시물의 끝에 제공됩니다.

범위의 단항 변환에 대한 가장 일반적인 구현은 표준 라이브러리의 일부입니다. 즉 std::transform, 반복 유형과 관련하여 템플릿 화됩니다.

template<class InputIt, class OutputIt, class UnaryOperation>
OutputIt transform(InputIt first1, InputIt last1, OutputIt d_first,
  UnaryOperation unary_op)
{
  while (first1 != last1) {
    *d_first++ = unary_op(*first1++);
  }
  return d_first;
}

4.2 템플릿 콜백 및 호환 유형을 사용하는 예

템플릿 std::function콜백 메소드 의 호환 가능한 유형 stdf_transform_every_int_templ은 위에서 언급 한 유형과 동일합니다 (3.4 참조).

그러나 템플릿 버전을 사용하면 사용 된 콜백의 서명이 약간 변경 될 수 있습니다.

// Let
int foo (int x) { return 2+x; }
int muh (int const &x) { return 3+x; }
int & woof (int &x) { x *= 4; return x; }

int a[5] = {1, 2, 3, 4, 5};
stdf_transform_every_int_templ<int,int>(&a[0], 5, &foo);
// a == {3, 4, 5, 6, 7}
stdf_transform_every_int_templ<int, int const &>(&a[0], 5, &muh);
// a == {6, 7, 8, 9, 10}
stdf_transform_every_int_templ<int, int &>(&a[0], 5, &woof);

참고 : std_ftransform_every_int(템플릿이 아닌 버전; 위 참조)은 작동 foo하지만 작동 하지 않습니다 muh.

// Let
void print_int(int * p, unsigned const n)
{
  bool f{ true };
  for (unsigned i = 0; i < n; ++i)
  {
    std::cout << (f ? "" : " ") << p[i];
    f = false;
  }
  std::cout << "\n";
}

일반 템플릿 매개 변수는 transform_every_int_templ가능한 모든 호출 가능한 유형이 될 수 있습니다.

int a[5] = { 1, 2, 3, 4, 5 };
print_int(a, 5);
transform_every_int_templ(&a[0], 5, foo);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, muh);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, woof);
print_int(a, 5);
transform_every_int_templ(&a[0], 5, [](int x) -> int { return x + x + x; });
print_int(a, 5);
transform_every_int_templ(&a[0], 5, Meow{ 4 });
print_int(a, 5);
using std::placeholders::_1;
transform_every_int_templ(&a[0], 5, std::bind(foo_2, _1, 3));
print_int(a, 5);
transform_every_int_templ(&a[0], 5, std::function<int(int)>{&foo});
print_int(a, 5);

위 코드는 다음과 같이 인쇄됩니다.

1 2 3 4 5
transform_every_int_templ <int(*)(int)>
3 4 5 6 7
transform_every_int_templ <int(*)(int&)>
6 8 10 12 14
transform_every_int_templ <int& (*)(int&)>
9 11 13 15 17
transform_every_int_templ <main::{lambda(int)#1} >
27 33 39 45 51
transform_every_int_templ <Meow>
108 132 156 180 204
transform_every_int_templ <std::_Bind<int(*(std::_Placeholder<1>, int))(int, int)>>
975 1191 1407 1623 1839
transform_every_int_templ <std::function<int(int)>>
977 1193 1409 1625 1841

type_name 위에서 사용 된 구현

#include <type_traits>
#include <typeinfo>
#include <string>
#include <memory>
#include <cxxabi.h>

template <class T>
std::string type_name()
{
  typedef typename std::remove_reference<T>::type TR;
  std::unique_ptr<char, void(*)(void*)> own
    (abi::__cxa_demangle(typeid(TR).name(), nullptr,
    nullptr, nullptr), std::free);
  std::string r = own != nullptr?own.get():typeid(TR).name();
  if (std::is_const<TR>::value)
    r += " const";
  if (std::is_volatile<TR>::value)
    r += " volatile";
  if (std::is_lvalue_reference<T>::value)
    r += " &";
  else if (std::is_rvalue_reference<T>::value)
    r += " &&";
  return r;
}

답변

콜백을 수행하는 C 방법도 있습니다 : 함수 포인터

//Define a type for the callback signature,
//it is not necessary, but makes life easier

//Function pointer called CallbackType that takes a float
//and returns an int
typedef int (*CallbackType)(float);


void DoWork(CallbackType callback)
{
  float variable = 0.0f;

  //Do calculations

  //Call the callback with the variable, and retrieve the
  //result
  int result = callback(variable);

  //Do something with the result
}

int SomeCallback(float variable)
{
  int result;

  //Interpret variable

  return result;
}

int main(int argc, char ** argv)
{
  //Pass in SomeCallback to the DoWork
  DoWork(&SomeCallback);
}

이제 클래스 메소드를 콜백으로 전달하려면 해당 함수 포인터에 대한 선언에 더 복잡한 선언이 있습니다. 예를 들면 다음과 같습니다.

//Declaration:
typedef int (ClassName::*CallbackType)(float);

//This method performs work using an object instance
void DoWorkObject(CallbackType callback)
{
  //Class instance to invoke it through
  ClassName objectInstance;

  //Invocation
  int result = (objectInstance.*callback)(1.0f);
}

//This method performs work using an object pointer
void DoWorkPointer(CallbackType callback)
{
  //Class pointer to invoke it through
  ClassName * pointerInstance;

  //Invocation
  int result = (pointerInstance->*callback)(1.0f);
}

int main(int argc, char ** argv)
{
  //Pass in SomeCallback to the DoWork
  DoWorkObject(&ClassName::Method);
  DoWorkPointer(&ClassName::Method);
}

답변

Scott Meyers는 좋은 예입니다.

class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);

class GameCharacter
{
public:
  typedef std::function<int (const GameCharacter&)> HealthCalcFunc;

  explicit GameCharacter(HealthCalcFunc hcf = defaultHealthCalc)
  : healthFunc(hcf)
  { }

  int healthValue() const { return healthFunc(*this); }

private:
  HealthCalcFunc healthFunc;
};

나는 그 예가 전부라고 생각합니다.

std::function<> C ++ 콜백을 작성하는 “현대적인”방법입니다.


답변

콜백 함수 가 전달되는 루틴에 의해 어떤 점에서 루틴에 전달하고, 호출되는 방법이다.

이것은 재사용 가능한 소프트웨어를 만드는 데 매우 유용합니다. 예를 들어, 많은 운영 체제 API (예 : Windows API)는 콜백을 많이 사용합니다.

예를 들어, 폴더의 파일로 작업하려는 경우 자신의 루틴으로 API 함수를 호출 할 수 있으며 지정된 폴더의 파일마다 루틴이 한 번 실행됩니다. 이를 통해 API는 매우 유연합니다.


답변

허용되는 답변은 매우 유용하고 포괄적입니다. 그러나 OP 상태

콜백 함수를 작성하는 간단한 예 를보고 싶습니다 .

C ++ 11부터 std::function함수 포인터와 비슷한 것들이 필요하지 않습니다.

#include <functional>
#include <string>
#include <iostream>

void print_hashes(std::function<int (const std::string&)> hash_calculator) {
    std::string strings_to_hash[] = {"you", "saved", "my", "day"};
    for(auto s : strings_to_hash)
        std::cout << s << ":" << hash_calculator(s) << std::endl;
}

int main() {
    print_hashes( [](const std::string& str) {   /** lambda expression */
        int result = 0;
        for (int i = 0; i < str.length(); i++)
            result += pow(31, i) * str.at(i);
        return result;
    });
    return 0;
}

이 예제는 print_hashes다른 방식으로 해시 함수의 다른 구현으로 함수를 호출하려고하기 때문에 어쨌든 실제 입니다.이 목적을 위해 간단한 것을 제공했습니다. 문자열을 수신하고 int (제공된 문자열의 해시 값)를 리턴하며 구문 부분에서 기억해야 할 std::function<int (const std::string&)>것은 해당 함수를 호출하는 함수의 입력 인수로 설명합니다.


답변

C ++에는 명시적인 콜백 함수 개념이 없습니다. 콜백 메커니즘은 종종 함수 포인터, functor 객체 또는 콜백 객체를 통해 구현됩니다. 프로그래머는 명시 적으로 콜백 기능을 설계하고 구현해야합니다.

피드백을 기반으로 편집 :

이 답변에 대한 부정적인 피드백에도 불구하고 잘못된 것은 아닙니다. 나는 어디에서 왔는지 설명하는 더 나은 일을하려고 노력할 것이다.

C 및 C ++에는 콜백 함수를 구현하는 데 필요한 모든 것이 있습니다. 콜백 함수를 구현하는 가장 일반적이고 사소한 방법은 함수 포인터를 함수 인수로 전달하는 것입니다.

그러나 콜백 함수와 함수 포인터는 동의어가 아닙니다. 함수 포인터는 언어 메커니즘이며 콜백 함수는 의미 개념입니다. 함수 포인터는 콜백 함수를 구현할 수있는 유일한 방법은 아닙니다. 또한 펑터와 정원 다양한 가상 함수를 사용할 수도 있습니다. 함수 호출을 콜백으로 만드는 것은 함수를 식별하고 호출하는 데 사용되는 메커니즘이 아니라 호출의 컨텍스트 및 의미입니다. 콜백 함수라고 말하는 것은 호출 기능과 호출되는 특정 기능 사이의 정상적인 분리, 호출자와 호출 수신자 사이의 느슨한 개념적 결합, 호출자가 호출되는 것을 명시 적으로 제어 할 수 있음을 의미합니다.

예를 들어, IFormatProvider 의 .NET 설명서에 따르면 “GetFormat은 콜백 메서드” 라고 말하지만 방금 다룬 인터페이스 방법 일뿐입니다. 나는 모든 가상 메소드 호출이 콜백 함수라고 주장하는 사람은 없다고 생각합니다. GetFormat을 콜백 메소드로 만드는 것은 그것이 전달되거나 호출되는 방식의 메커니즘이 아니라 호출자의 시맨틱이 어떤 오브젝트의 GetFormat 메소드가 호출 될지를 선택하는 것입니다.

일부 언어에는 일반적으로 이벤트 및 이벤트 처리와 관련된 명시적인 콜백 시맨틱 기능이 있습니다. 예를 들어 C #에는 콜백 개념을 중심으로 명시 적으로 설계된 구문 및 의미 체계 가있는 이벤트 유형이 있습니다. Visual Basic에는 Handles 절이 있습니다.이 절은 대리자 또는 함수 포인터의 개념을 추상화하면서 메서드를 콜백 함수로 명시 적으로 선언합니다. 이 경우 콜백의 시맨틱 개념이 언어 자체에 통합됩니다.

반면에 C와 C ++ 는 콜백 함수 의 시맨틱 개념 을 거의 명시 적으로 포함하지 않습니다 . 메커니즘이 있으며 통합 의미론은 없습니다. 콜백 함수를 잘 구현할 수는 있지만 명시적인 콜백 의미를 포함하는 더 정교한 것을 얻으려면 Qt가 Signals and Slots 와 같이 C ++에서 제공하는 것 위에 빌드해야합니다 .

간단히 말해서 C ++에는 함수 포인터를 사용하여 쉽고 간단하게 콜백을 구현하는 데 필요한 것이 있습니다. 그것이 가지고 있지 않은 것은 raise , emit , Handles , event + = 등과 같은 콜백에 고유 한 의미를 가진 키워드 및 기능입니다 . 이러한 유형의 요소가있는 언어에서 오는 경우 C ++의 기본 콜백 지원 중성 느낄 것입니다.


답변

콜백 함수는 C 표준의 일부이므로 C ++의 일부입니다. 그러나 C ++로 작업하는 경우 옵저버 패턴을 대신 사용하는 것이 좋습니다 . http://en.wikipedia.org/wiki/Observer_pattern