std :: function 및 std :: bind : 그것들은 무엇이며 언제 사용해야합니까?


128

펑터가 무엇이며 언제 std알고리즘 과 함께 사용해야하는지 알고 있지만 C ++ 11 FAQ 에서 Stroustrup이 그들에 대해 말하는 것을 이해하지 못했습니다 .

누구든지 무엇이 무엇 std::bind이고 std::function언제 사용되어야 하는지 설명하고 초보자를위한 몇 가지 예를 제공 할 수 있습니까?

답변:


201

std::bind부분 기능 적용을 위한 것 입니다.

즉, f3 개의 인수를 받는 함수 객체가 있다고 가정합니다 .

f(a,b,c);

다음과 같이 정의 된 두 개의 인수 만받는 새 함수 객체를 원합니다.

g(a,b) := f(a, 4, b);

g은 함수의 "부분적 응용"입니다 f. 중간 인수가 이미 지정되었으며 두 개가 남아 있습니다.

다음 std::bind을 얻는 데 사용할 수 있습니다 g.

auto g = bind(f, _1, 4, _2);

이것은 실제로 펑터 클래스를 작성하는 것보다 더 간결합니다.

링크하는 기사에 추가 예제가 있습니다. 일반적으로 일부 알고리즘에 펑터를 전달해야 할 때 사용합니다. 원하는 작업 을 거의 수행하지만 알고리즘이 사용하는 것보다 더 많은 구성 가능 (즉, 더 많은 매개 변수가 있음) 기능 또는 펑터 가 있습니다. 따라서 인수를 일부 매개 변수에 바인딩하고 나머지는 알고리즘이 채울 수 있도록 둡니다.

// raise every value in vec to the power of 7
std::transform(vec.begin(), vec.end(), some_output, std::bind(std::pow, _1, 7));

여기에서 pow두 개의 매개 변수를 취하고 어떤 거듭 제곱 으로 올릴 수 있지만 우리가 신경 쓰는 것은 7 제곱까지 올리는 것뿐입니다.

부분 함수 응용 프로그램이 아닌 가끔 사용하는 경우 bind인수를 함수에 다시 정렬 할 수도 있습니다.

auto memcpy_with_the_parameters_in_the_right_flipping_order = bind(memcpy, _2, _1, _3);

API가 마음에 들지 않는다고해서 사용하지 않는 것이 좋지만 다음과 같은 이유로 잠재적 인 실용적인 용도가 있습니다.

not2(bind(less<T>, _2, _1));

이 함수는 작거나 같은 함수입니다 (총 주문을 가정하면 어쩌고). 이 예제는 일반적으로 필요하지 않습니다. 이미있는 경우 std::less_equal( <=대신 연산자를 사용 <하므로 일관성이 없으면 이것이 필요할 수 있으며 단서로 클래스 작성자를 방문해야 할 수도 있습니다). 하지만 함수형 프로그래밍을 사용하는 경우 나타나는 일종의 변형입니다.


18
멤버 함수에 대한 콜백에도 유용합니다.myThread=boost::thread(boost::bind(&MyClass::threadMain, this))
rlduffy 2011-07-09

15
바인드에 대한 좋은 설명. 하지만 std::function어떨까요?
RedX

10
귀하의 pow예제는 컴파일되지 않습니다. pow오버로드 된 함수 이므로 어떤 오버로드를 수동으로 지정해야합니다. 바인딩은 결과 펑터의 호출자가 추론하도록 둘 수 없습니다. 예std::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
MM

2
매우 잘 설명되어 있지만 때로는 두 번째 인수로 사용법과 std::bind함께 제공됩니다 this. 이 사용 사례를 자세히 설명해 주시겠습니까?
Mendes

2
또한 "_1"은을 의미 std::placeholders::_1합니다. 이것이 컴파일되지 않은 이유를 알아내는 데 시간이 걸렸습니다.
terryg

26

std :: function 및 std :: bind의 주요 용도 중 하나는 더 많은 유전자 관련 함수 포인터입니다. 콜백 메커니즘을 구현하는 데 사용할 수 있습니다. 인기있는 시나리오 중 하나는 실행하는 데 오랜 시간이 걸리는 함수가 있지만 반환 될 때까지 기다리지 않고 별도의 스레드에서 해당 함수를 실행하고 함수 포인터를 제공 할 수 있다는 것입니다. 완료 후 콜백.

이를 사용하는 방법에 대한 샘플 코드는 다음과 같습니다.

class MyClass {
private:
    //just shorthand to avoid long typing
    typedef std::function<void (float result)> TCallback;

    //this function takes long time
    void longRunningFunction(TCallback callback)
    {
        //do some long running task
        //...
        //callback to return result
        callback(result);
    }

    //this function gets called by longRunningFunction after its done
    void afterCompleteCallback(float result)
    {
        std::cout << result;
    }

public:
    int longRunningFunctionAsync()
    {
        //create callback - this equivalent of safe function pointer
        auto callback = std::bind(&MyClass::afterCompleteCallback, 
            this, std::placeholders::_1);

        //normally you want to start below function on seprate thread, 
        //but for illustration we will just do simple call
        longRunningFunction(callback);
    }
};

5
이것은 훌륭한 대답입니다. 나는이 답을 찾기 위해 모든 것을 살펴 보았다. 감사 @ShitalShah
terryg

바인딩이 더 안전한 이유에 대한 설명을 추가 할 수 있습니까?
Steven Lu

내 잘못이야 ... 더 "더 안전하다"고 말할 생각은 없었습니다. 일반 함수 포인터는 형태 보증 그러나 수 std된다 :: 기능 등 람다와 작업, 컨텍스트 캡처, 멤버 방법에 대한보다 일반적인입니다
Shital 샤

bind (& MyClass :: afterCompleteCallback, this, std :: placeholders :: _ 1), 정의에서 1에 대한 2 개의 인수, void afterCompleteCallback (float result), 이것을 설명 할 수 있습니까?
노녹

1
@nonock 멤버 함수의 함수 포인터의 경우 "this"포인터를 첫 번째 인수로 전달해야합니다.
sanoj subran

12

std :: bind는 부스트 바인드를 포함하도록 제안한 후 라이브러리에 투표되었습니다. 주로 일부 매개 변수를 수정하고 다른 매개 변수를 즉시 변경할 수있는 부분 함수 전문화입니다. 이제 이것은 C ++에서 람다를 수행하는 라이브러리 방식입니다. Steve Jessop의 답변

이제 C ++ 11이 람다 함수를 지원하므로 더 이상 std :: bind를 사용하고 싶은 유혹을 느끼지 않습니다. 나는 도서관 기능보다는 언어 기능이있는 카레 (부분 전문화)를 사용하고 싶다.

std :: function 객체는 다형성 함수입니다. 기본 아이디어는 모든 호출 가능한 객체를 상호 교환 적으로 참조 할 수 있다는 것입니다.

자세한 내용은 다음 두 링크를 알려 드리겠습니다.

C ++ 11의 Lambda 함수 : http://www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8

C ++의 호출 가능 엔티티 : http://www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8


5
std::bind람다 없이는 존재하지 않았습니다. 두 기능 모두 C ++ 11에 도입되었습니다. 우리는 있었나요 bind1st그리고 bind2nd이는 C ++ (11) 바인딩의 버전을 쇠약 케했다.
MM

5

나는 C ++에서 플러그인 스레드 풀을 만들기 위해 오랫동안 사용했다. 함수가 세 개의 매개 변수를 취했기 때문에 다음과 같이 작성할 수 있습니다.

메서드에 서명이 있다고 가정합니다.

int CTask::ThreeParameterTask(int par1, int par2, int par3)

세 매개 변수를 바인딩하는 함수 객체를 만들려면 다음과 같이 할 수 있습니다.

// a template class for converting a member function of the type int function(int,int,int)
//to be called as a function object
template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
class mem_fun3_t
{
public:
    explicit mem_fun3_t(_Ret (_Class::*_Pm)(_arg1,_arg2,_arg3))
        :m_Ptr(_Pm) //okay here we store the member function pointer for later use
    {}

    //this operator call comes from the bind method
    _Ret operator()(_Class *_P, _arg1 arg1, _arg2 arg2, _arg3 arg3) const
    {
        return ((_P->*m_Ptr)(arg1,arg2,arg3));
    }
private:
    _Ret (_Class::*m_Ptr)(_arg1,_arg2,_arg3);// method pointer signature
};

이제 매개 변수를 바인딩하기 위해 바인더 함수를 작성해야합니다. 그래서 여기에 간다 :

template<typename _Func,typename _Ptr,typename _arg1,typename _arg2,typename _arg3>
class binder3
{
public:
    //This is the constructor that does the binding part
    binder3(_Func fn,_Ptr ptr,_arg1 i,_arg2 j,_arg3 k)
        :m_ptr(ptr),m_fn(fn),m1(i),m2(j),m3(k){}


        //and this is the function object 
        void operator()() const
        {
            m_fn(m_ptr,m1,m2,m3);//that calls the operator
        }
private:
    _Ptr m_ptr;
    _Func m_fn;
    _arg1 m1; _arg2 m2; _arg3 m3;
};

그리고 binder3 클래스를 사용하는 도우미 함수-bind3 :

//a helper function to call binder3
template <typename _Func, typename _P1,typename _arg1,typename _arg2,typename _arg3>
binder3<_Func, _P1, _arg1, _arg2, _arg3> bind3(_Func func, _P1 p1,_arg1 i,_arg2 j,_arg3 k)
{
    return binder3<_Func, _P1, _arg1, _arg2, _arg3> (func, p1,i,j,k);
}

그리고 여기서 우리는 그것을 부르는 방법

F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3( 
          &CTask::ThreeParameterTask), task1,2122,23 );

참고 : f3 (); task1-> ThreeParameterTask (21,22,23) 메소드를 호출합니다.

자세한 내용은-> http://www.codeproject.com/Articles/26078/AC-Plug-in-ThreadPool-Design

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.