[C++]std::reference_wrapper와 std::ref

jh Seo·2023년 5월 29일
0

C++공부

목록 보기
16/21

개요

apple이란 클래스가 있을 때, apple클래스를 담는 벡터를 선언하다가,
클래스의 레퍼런스를 담은 벡터도 가능한가 궁금했다.

vector<apple&> v;

이런식으로 구현하니 오류가 떴다.

이런식으로 참조에 대한 포인터를 만들 수 없다고 나온다.

궁금해서 검색해봤더니 기본적으로 벡터를 선언할 때 가능한 템플릿 자료형들은 assign과 copy 즉 할당과 복사가 가능한 자료형들이여야 한다.

하지만 참조자는 alias의 성질로 선언과 동시에 할당을 받아야한다. 따라서 혼자 쓰이면 메모리를 할당받지 않으므로 사용할 수 없는 것이였다.

std::reference_wrapper

이럴때 std::reference_wrapper를 사용한다.
헤더파일은 #include<functional> 이다
reference_wrapper은

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.

읽어보면 레퍼런스를 copy가능하고 assign가능하게 래핑을 해준다.
바로 다음 예시에 쓰여있듯이 STL라이브러리의 컨테이너들은 copyable, ssignable한 객체만 자료형으로 사용가능한데
이 컨테이너들에서도 wrapping을 통해 참조자의 형태로 사용할수 있게 한다.

따라서

vector<reference_wrapper<apple> > v;

이런식으로 구현을 하고 apple클래스들을 담으면 각 클래스들이 참조자 형태로 저장이 되어서 복사가 일어나지 않는다.
reference_wrapper도 참조형이라

reference_wrapper<apple> a;
v.push_back(a);

이런식으로 짜면 에러가난다. 참조형의 특성으로 선언과 동시에 할당해줘야한다.
저렇게 default값으로 nullptr이 들어갈 수 없다.

이렇게 vector<reference_wrapper<apple> >v에 apple객체들을
참조형으로 push_back()하려면 밑에 나오는 std::ref를 사용한다.

예시로는 using namespace std; 작성했을 때

	apple a(1);
	apple b(2);
	vector<reference_wrapper< apple>> v;

	v.push_back(ref(a));
	v.push_back(ref(b));

	for (apple& elem : v)
	{
		cout<< elem.data<<'\n';
	}

이런식으로 가능하다.

std::ref

std::ref도 마찬가지로 #include<functional>에 저장되어있는 친구다.
ref함수는 인자로 전달받은 값을 복사가능한 레퍼런스로 만들어준다.

전에 작성한 글 std::function과 std::bind 에 설명한 std::bind()와
많이 사용한다.

이런 apple클래스가 있다고 하자.

class apple {
public:
	int data;
	apple(int tmpData):data(tmpData) {
		cout<<"Init"<<'\n';
	}
	~apple() {
		cout <<data <<" deleting" << '\n';
	}
	apple(const apple& other) {
		cout << "Copying"<<'\n';
		data = other.data;
	}
	void Shout(int x,int y) {
		cout << "I have" <<x+y<< "apples!!!!" << '\n';
	}
	void BindShout(function<void(int)>& other) {
		other = std::bind(&apple::Shout, this,std::placeholders::_1, 2);
	}
	
};

두 apple클래스를 받아서 a의 data에 b의 데이터를 넣어주는 Test함수를 작성했다.

void Test(apple& a, const apple& b) {
	a.data += b.data;
}

using namepspace std;를 선언한 후 main을 이런식으로 작성했을 때,

int main() {
	apple a(1);
	apple b(2);

	function<void( const apple&)> f = bind(Test, a, placeholders::_1);
	f(b);
	cout << a.data<<'\n';

a.data로 1+2가 되어 3이 출력되어야하지만 기존값인 1이 출력된다.

콘솔에서 보다시피 이미 복사생성자가 생기는 점에서 우린 a와 b가 복사되어서 f함수를 실행하고 사라진걸 알 수 있다.

따라서 a의 레퍼런스값을 넘겨줘야하는데 이때 std::ref함수를 사용할 수 있다.

int main() {
	apple a(1);
	apple b(2);

	function<void( const apple&)> f = bind(Test, ref(a), placeholders::_1);
	f(b);
	cout << a.data<<'\n';

이런식으로 수정하고 실행한다면

복사가 일어나지 않는다.

레퍼런스

https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper

profile
코딩 창고!

0개의 댓글