본문 바로가기
Dev Language/Modern C++ (C++11, 14)

C++ 11 : std::function 함수 객체? 함수면 함수고 객체면 객체인데, 함수 객체는 무엇인가 ?

by 미티치 2020. 8. 8.

std::function은 C++11 표준 라이브러리의 한 템플릿으로, 함수 포인터 개념을 일반화한 것 이다.

 


C++에서는 'Callable'들을 객체의 형태로 보관할 수 있는 std::funciton 이라는 클래스를 제공한다. C에서의 함수 포인터는 진짜 함수들만 보관할 수 있는 객체라고 보았다면, 이 std::function은 함수 뿐만 아니라 모든 Callable들을 보관할 수 있다.

 

그럼 Callable은 무엇인가?

C++에서 호출 가능한 모든 것을 포괄해서 나타내는 것이다. C++에서 ()를 붙여서 호출할 수 있는 모든 것을 Callable이라고 한다.

typedef struct _s_oper
{
         void operator() ( int nA, int nB )     {
                 std::cout << "nA * nB = " << nA*nB << std::endl;
         }
}S_oper;

auto _tmain(int argc, _TCHAR* argv[]) -> int
{
         S_oper s;
         s(3,5);         // 실제로는 s.operator() (3,5);
 
        /* 위에서 s는 마치 함수처럼 호출되지만 함수가 아니다.
         엄밀히 말하면 S_oper의 인스턴스, 즉 객체이다.
         */
}

C++의 Callable의 개념을 알았다면 다시 std::function으로 돌아오자. std::function 은 이러한 Callable 속성을 가진 모든 것들을 객체 형태로 보관할 수 있는 객체이다. 

 

위의 예시에서처럼 구조체 내에 정의한 함수의 주소도 받을 수 있고, 일반적으로 선언한 함수의 주소도 받을 수 있다.

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <functional>

void func_oper(int nA, int nB)
{
        std::cout << "func_oper : nA * nB = " << nA*nB << std::endl;
}
typedef struct _s_oper
{
    void operator() ( int nA, int nB )     {    std::cout << "s_oper : nA * nB = " << nA*nB << std::endl;        }
}S_oper;

auto _tmain(int argc, _TCHAR* argv[]) -> int
{
    S_oper s

    std::function< void(int, int) > func1 = func_oper;       // func1에 func_oper의 주소를 저장
    std::function< void(int, int) > func2 = S_oper();        // func2에 S_oper의 () operator의 주소를 저장

    func1(10,20);
    func2(30,40);
}

 

 

 


하지만 function은 일반적인 Callable 들을 보관할 수 있지만, 멤버함수의 경우는 얘기가 달라진다.

왜냐햐면 멤버 함수 내에서는 클래스의 내부 데이터에 접근하는데 (*만약 내부 데이터에 접근하지 않는다! 라고 하면 클래스로 정의하지말고 함수 포인터만 갖는 구조체로 정의하는게 나을 것이다.)  멤버 함수에 접근할 때 (명시적으로든 암묵적으로든 호출되는) this의 경우 자신을 호출한 객체를 의미하기 때문에, std::function에 멤버 함수를 넣으면 this가 무엇인지 알 방법이 없다.

따라서 std::function 으로 선언한 함수 객체에 멤버 함수 주소를 넣으면 컴파일 에러가 난다.

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <functional>

void func_print(int _a)
{
    std::cout << "func_print = " << _a << std::endl;
}
class CA
{
public:
    CA(int _Num) : m_Num(_Num) {};
    ~CA (){};

    void do_print()
    {
        std::cout << "this m_Num = " << m_Num << std::endl;
    }
private:
    int m_Num;
};

auto _tmain(int argc, _TCHAR* argv[]) -> int
{
    CA instA(5);
    std::function<void(int)> func1 = func_print;
    std::function<void(void) > func2 = &CA::do_print;    // compile error

}

그럼 어떻게 해야하나?
원래 인자에 추가적으로 객체를 받는 인자를 전달해주면 된다.

#include "stdafx.h"
#include <iostream>
#include <stdio.h>
#include <functional>


void func_print(int _a)
{
    std::cout << "func_print = " << _a << std::endl;
}


class CA
{
public:
    CA(int _Num) : m_Num(_Num) {};
    ~CA (){};


    void do_print(int _n)
    {
        std::cout << "this parameter = " << _n << std::endl;
        std::cout << "this m_Num = " << m_Num << std::endl;
    }
private:
    int m_Num;
};

auto _tmain(int argc, _TCHAR* argv[]) -> int
{
    std::function<void(int)> func1 = func_print;


    CA instA(5);
    std::function< void(CA& , int) > func2 = &CA::do_print;    // 컴파일 오류... 응?
    func2(instA, 10);
}

뭐야 컴파일 된다매 ㅠㅠ 왜 안되냐 ㅠㅠ 근데 오류가 템플릿에 인자 잘못전달했다는 오류라서 알아볼 수가 없다
다시 템플릿 공부부터 해야 디버깅이 될 것 같다..

 

오류 내용

error C2664: 'std::_Func_class<_Ret,_V0_t,_V1_t>::_Set' : 매개 변수 1을(를) '_Myimpl *'에서 'std::_Func_base<_Rx,_V0_t,_V1_t> *'(으)로 변환할 수 없습니다.

1>          with

1>          [

1>              _Ret=void,

1>              _V0_t=CA &,

1>              _V1_t=int

1>          ]

1>          and

1>          [

1>              _Rx=void,

1>              _V0_t=CA &,

1>              _V1_t=int

1>          ]

1>          가리킨 형식이 관련이 없습니다. 변환하려면 reinterpret_cast, C 스타일 캐스트 또는 함수 스타일 캐스트가 필요합니다.

 

'Dev Language > Modern C++ (C++11, 14)' 카테고리의 다른 글

C++ 11 : 열거타입 enum  (0) 2020.08.08
C++ 11 : nullptr  (0) 2020.08.08
C++ 11 : auto_ptr  (0) 2020.08.08
C++ 11 : unique_ptr  (0) 2020.08.08
C++ 11 : shared_ptr  (0) 2020.08.08