C++ 중급 - 멤버 변수의 포인터

타입·2024년 1월 18일
0

C++ 공부

목록 보기
6/17

멤버 데이터 포인터

  • Pointer To Member Data
    멤버 데이터를 가리키는 포인터
struct Point
{
	int x;
	int y;
};
int main()
{
	int Point::*p2 = &Point::y;
}

p2는 Point 구조체 안에서 y의 offset(4)을 담고 있음 (int는 4byte이므로)

int main()
{
	...
//	*p2 = 10; // error, p2를 바로 쓸 순 없음
    
	Point pt;
	pt.*p2 = 10; // pt.y = 10
				 // *((char*)&pt + p2) = 10; // pt의 주소에서 p2의 오프셋만큼 더하여 접근
    cout << p2 << std::endl; // 1 cout은 이상하게 나옴
    printf("%p\n", p2); // 4
}
  • 멤버 데이터 포인터로 객체의 멤버 데이터에 접근하는 방법
  1. Pointer to Member 연산자 [ .*, ->* ]
  2. std::invoke()
int main()
{
	int Point::*p = &Point::y;

	Point obj;

	obj.*p = 10; // obj.y = 10
	(&obj)->*p = 10;

	std::invoke(p, obj) = 20; // obj.y = 20
	int n = std::invoke(p, obj); 

	std::cout << n << std::endl;
	std::cout << obj.y << std::endl;
}

Callable Type: std::invoke()를 사용할 수 있는 타입
std::invoke()에 멤버 함수 포인터 뿐만 아니라 멤버 데이터 포인터도 넣을 수 있음


max 함수 구현

문자열의 크기 비교

template<class T> 
const T& mymax(const T& obj1, const T& obj2)
{
	return obj1 < obj2 ? obj2 : obj1;
}

int main()
{
	std::string s1 = "abcd";
	std::string s2 = "xyz";

	auto ret = mymax(s1, s2); // "xyz"
}
  1. 이항 조건자 (Binary Predicator) 사용
    문자열의 길이로 비교하고 싶다면
auto ret = mymax(s1, s2, [](auto& a, auto& b) { return a.size() < b.size(); } );
  1. 단항 조건자 (Unary Predicator) 사용
    함수 결과값으로 비교
auto ret = mymax(s1, s2, [](auto& a) { return a.size(); } ); // f(s1) < f(s2)
  1. 둘 다 사용
    C++20 Range Algorithm의 원리
    멤버 함수 포인터, 멤버 데이터 포인터, std::invoke 활용
  • Projection 개념 추가
    std::invoke() 함수로 일반 함수와 멤버 함수 포인터 모두 사용 가능
#include <functional>

template<class T,  class Proj> 
const T& mymax(const T& obj1, const T& obj2, Proj proj)
{
//	return proj(obj1) < proj(obj2) ? obj2 : obj1;
					// (obj2.*proj)() // 이러면 멤버 함수 포인터만 가능

	return std::invoke(proj, obj1) < std::invoke(proj, obj2) ? obj2 : obj1;
}
int main()
{
	std::string s1 = "abcd";
	std::string s2 = "xyz";

	auto ret1 = mymax(s1, s2, [](auto& a) { return a.size(); } ); // 단항 조건자
	auto ret2 = mymax(s1, s2, &std::string::size); // 멤버 함수 포인터 전달
}
  • Projection 디폴트 처리
template<class T,  class Proj = std::identity> 
const T& mymax(const T& obj1, const T& obj2, Proj proj = {} )
{
	// proj 인자가 없다면 Proj 타입을 std::identity로 만들고
    // proj는 std::identity의 디폴트 객체로 생성
	return std::invoke(proj, obj1) < std::invoke(proj, obj2) ? obj2 : obj1;
}
int main()
{
	std::string s1 = "abcd";
	std::string s2 = "xyz";

	auto ret = mymax(s1, s2);
}

std::identity(): 전달받은 인자를 어떠한 변화도 없이 그대로 반환하는 함수객체 (C++20)

  • 멤버 변수 포인터 사용
struct Point
{
	int x, y;
};
int main()
{
	Point p1 = {2, 0};
	Point p2 = {1, 1};

	auto ret = mymax(p1, p2, &Point::y); // y값으로 비교
}

mymax() 함수의 std::invoke(proj, obj1)에서 멤버 변수 포인터를 사용하면 (obj1.*proj)로 동작


  • 비교정책도 교체
template<class T, class Comp = std::less<void>, class Proj = std::identity> 
const T& mymax(const T& obj1, const T& obj2, Comp comp = {}, Proj proj = {})
{
//	return std::invoke(proj, obj1) < std::invoke(proj, obj2) ? obj2 : obj1;
//	return comp(std::invoke(proj, obj1), std::invoke(proj, obj2)) ? obj2 : obj1; // 멤버함수일 땐 에러발생하여 invoke로 감싸기
	return std::invoke(comp, std::invoke(proj, obj1), std::invoke(proj, obj2)) ? obj2 : obj1;
}
int main()
{
	std::string s1 = "abcd";
	std::string s2 = "xyz";

	auto ret1 = mymax(s1, s2); // 객체만 전달
	auto ret2 = mymax(s1, s2, std::greater{}); // 비교 함수객체 전달
	auto ret3 = mymax(s1, s2, {},  &std::string::size); // projection 전달
	auto ret4 = mymax(s1, s2, std::greater{},  &std::string::size); // 비교 함수객체, projection 모두 전달
}
  • Range Algorithm (C++20)
    알고리즘의 비교 정책 교체 가능
    Projection 전달 가능
auto ret = std::ranges::max(s1, s2, std::ranges::greater{},  &std::string::size);

std::range::max는 함수 객체(템플릿)

반복자 구간이 아닌 컨테이너를 전달받음

std::vector<std::string> v = { "hello", "a", "xxx", "zz" };

//	std::sort(v.begin(), v.end());
//	std::ranges::sort(v);
	std::ranges::sort(v, std::ranges::greater{}, &std::string::size);
profile
주니어 언리얼 프로그래머

0개의 댓글