C++

03. C++_invoke

만수르코딩방 2024. 3. 24. 23:18

일반적으로 클래스의 멤버함수를 호출할 때 멤버함수와 함께 객체의 정보가 함께 전달됩니다. 따라서 static 이 아닌 멤버 함수는 함수가 호출될 때 컴파일러가 객체의 객체의 정보 또한 함수의 인자로 전달을 하는데 이것을 thiscall이라고 합니다.  예를 들어 인자가 1개인 멤버 함수 mf1(int a)가 있다고 했을 때, 실제 객체가 함수를 호출할 때에는 mf1(&pt, int a)와 같이 2개의 인자를 전달하는 형태로 바뀝니다.  멤버 데이터는 객체당 한개씩 생성되는 반면, 멤버 함수는 여러 객체가 있어도 하나만 존재하는데 컴파일러에서 멤버함수를 호출할 때 객체에 대한 정보를 보내주기 때문에 멤버함수의 인자가 어떤 객체의 멤버인지 알 수 있습니다. 

class Point
{
	int x{0};
	int y{0};
public:
	void mf1(int a, int b) //void mf1(Point* this, int a)
	{
		x = a; //this -> x = a;
		y = b; //this -> y = b; 
	}
	void sf1(int a)
	{
		x = a; //error
		y = b; //error
	}
};
//멤버함수 호출 시 객체의 정보(주소)가 같이 전달되는 것을 this call 이라고 한다. 

int main()
{
	Point pt1;
	Point pt2;

	pt1.mf1(10, 20); //Point::mf1(&pt1, 10, 20), pt1.x = 10 pt1.y = 20
	pt2.mf1(10, 20); //Point::mf1(&pt2, 10, 20), pt2.x = 10 pt2.y = 20
	pt1.sf1(10); 	 //Point::sf1(10) pt1 과 pt2 중 어떤 객체의 멤버?
}
//멤버함수는 객체를 여러개 만들어도 함수는 하나만 있어

 

위 예제와 같이 처럼 멤버함수를 호출할 때에는 객체의 정보가 같이 전달되기 때문에 멤보 함수 포인터를 사용해서 멤버 함수를 호출하는 방법과 일반함수 포인터를 사용해서 일반함수를 호출하는 방법은 다르게 동작합니다. 이를 통일된 형태로 호출하기 위해 제안된 문법으로 C++17 표준의 <functional> 헤더에 정의된 std::invoke라는 함수가 있습니다. 

 이 함수는 함수, 함수 포인터 등을 호출할 때 통일된 방식으로 사용될 수 있습니다. 또한  클래스에서 멤버함수 포인터, 멤버 변수를 호출하는 경우에도 유용하게 사용됩니다. std::invoke는 호출할 대상과 해당 대상에 전달할 인자들을 받아 호출합니다. 

#include <iostream>
#include <functional>

class X
{
public:
    void mf1(int a) { std::cout << "member function call" <<std::endl; }
    int md1;
};
void foo(int a) { std::cout<< "non-member function call function call" <<std::endl; }

int main()
{
    X obj;

    void(*f1)(int) = &foo;
    void(X::*f2)(int) = &X::mf1; 
    int(X::*f3) = &X::md1;
    //멤버함수, 멤버 데이터의 주소를 포인터에 매칭시킬 때에는 멤버함수의 클래스도 함께 정의해주어야 한다.

    f1(10);			// 일반 함수 포인터 사용
    (obj.*f2)(10);	// 멤버 함수 포인터 사용 1)클래스 선언, 2)멤버함수 포인터 사용
    obj.*f3 = 10;   // 멤버 데이터 포인터 사용 
    //멤버 함수와 멤버 데이터 포인터를 이용해서 대상을 호출할 때에는 대상이 속한 클래스의 객체가 먼저 선언되어야 한다. 

    std::invoke(f1, 10);       // f1(10)
    std::invoke(f2, obj,  10); // (obj.*f2)(10) 
    std::invoke(f2, &obj, 10); // (obj.*f2)(10)
    std::invoke(f3, &obj) = 10;// (obj.*f3 = 10)
    //invoke 함수를 이용하면 함수, 멤버 변수, 멤버 함수를 통일된 형식으로 호출 가능하다.

    std::cout << obj.md1 << std::endl;


}

 

위 예제는 강성민 강사님의 cpp intermediate 강좌의 내용을 인용하였습니다.

Course Status – ecourse 온라인 강의

invoke 문법 정의는 cppreference에서 확인하실 수 있습니다.

std::invoke, std::invoke_r - cppreference.com