함수 포인터보다 std::function 을 사용하는게 더 깔끔해 보이는 것 같다.


template< class R, class... Args >

class function<R (Args...)>;


선언은 반환 형식 R 을 적고 '(', ')' 괄호 안에 입력 형식을 적어준다. 함수, 람다 표현식, 멤버 함수를 대입해서 사용할 수 있다. delegate 를 구현할 때 많이 사용한다.


일반 함수 대입은 아래와 같은 선언하고 사용한다.


// store a free function

std::function<void(int)> f_display = print_num;

f_display(-9);


멤버 함수 대입은 아래와 같이 많이 한다.


struct Foo {

    Foo(int num) : num_(num) {}

    void print_add(int i) const { std::cout << num_+i << '\n'; }

    int num_;

};


// store a call to a member function and object

using std::placeholders::_1;

const Foo foo(314159);

std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );

f_add_display2(2);


다른 사용법도 있으니 아래 예제를 참고하자.


#include <functional>

#include <iostream>

 

struct Foo {

    Foo(int num) : num_(num) {}

    void print_add(int i) const { std::cout << num_+i << '\n'; }

    int num_;

};

 

void print_num(int i)

{

    std::cout << i << '\n';

}

 

struct PrintNum {

    void operator()(int i) const

    {

        std::cout << i << '\n';

    }

};

 

int main()

{

    // store a free function

    std::function<void(int)> f_display = print_num;

    f_display(-9);


    // store a lambda

    std::function<void()> f_display_42 = []() { print_num(42); };

    f_display_42();

 

    // store the result of a call to std::bind

    std::function<void()> f_display_31337 = std::bind(print_num, 31337);

    f_display_31337();

 

    // store a call to a member function

    std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;

    const Foo foo(314159);

    f_add_display(foo, 1);

    f_add_display(314159, 1);

 

    // store a call to a data member accessor

    std::function<int(Foo const&)> f_num = &Foo::num_;

    std::cout << "num_: " << f_num(foo) << '\n';

 

    // store a call to a member function and object

    using std::placeholders::_1;

    std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );

    f_add_display2(2);

 

    // store a call to a member function and object ptr

    std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );

    f_add_display3(3);

 

    // store a call to a function object

    std::function<void(int)> f_display_obj = PrintNum();

    f_display_obj(18);

}


위 예제에도 나왔지만 std::function 에 대입할 때 std::bind를 이용하게 된다. 

std::function 변수와 함수 선언 순서가 다른 경우에 std::bind 에 std::placeholders 이용해서 대입하는 경우가 많다.


아래 예제를 보면 a, b, c 를 받아서 std::bind 와 placeholders::_1, placeholders::_2 를 이용해 f( b, 42, a, n, 7) 로 호출하는 법을 알 수 있다. 


void f(int n1, int n2, int n3, const int& n4, int n5)

{

    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';

}


// demonstrates argument reordering and pass-by-reference

int n = 7;

// (_1 and _2 are from std::placeholders, and represent future

// arguments that will be passed to f1)

auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);

n = 10;

f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused

                   // makes a call to f(2, 42, 1, n, 7)


멤버 변수 객체를 묶어 숨겨서 호출하는 쪽에서 모르게 할 수 있어 delegate 만들 때 유용한 것 같다.


Foo foo;

auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);

f3(5);


다른 예제는 아래 예제를 참고하자.


#include <random>

#include <iostream>

#include <memory>

#include <functional>

 

void f(int n1, int n2, int n3, const int& n4, int n5)

{

    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';

}

 

int g(int n1)

{

    return n1;

}

 

struct Foo {

    void print_sum(int n1, int n2)

    {

        std::cout << n1+n2 << '\n';

    }

    int data = 10;

};

 

int main()

{

    using namespace std::placeholders;  // for _1, _2, _3...

 

    // demonstrates argument reordering and pass-by-reference

    int n = 7;

    // (_1 and _2 are from std::placeholders, and represent future

    // arguments that will be passed to f1)

    auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);

    n = 10;

    f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused

                    // makes a call to f(2, 42, 1, n, 7)

 

    // nested bind subexpressions share the placeholders

    auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);

    f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);

 

    // common use case: binding a RNG with a distribution

    std::default_random_engine e;

    std::uniform_int_distribution<> d(0, 10);

    auto rnd = std::bind(d, e); // a copy of e is stored in rnd

    for(int n=0; n<10; ++n)

        std::cout << rnd() << ' ';

    std::cout << '\n';

 

    // bind to a pointer to member function

    Foo foo;

    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);

    f3(5);

 

    // bind to a pointer to data member

    auto f4 = std::bind(&Foo::data, _1);

    std::cout << f4(foo) << '\n';

 

    // smart pointers can be used to call members of the referenced objects, too

    std::cout << f4(std::make_shared<Foo>(foo)) << '\n'

              << f4(std::make_unique<Foo>(foo)) << '\n';

}


참고

* std::bind

* std:function

728x90

+ Recent posts