본 글은 std::reference_wrapper 의 용도와 목적에 대한 글이기에
std::reference_wrapper 사용방법을 알고싶다면 다른 글에서 확인 부탁 드립니다.
필요성
thread 로 동작시켜야 할 함수(or 클래스)가 있는데 thread 내에서 계산을 하고 그 값을 main thread를 받아야 할 때 '전역변수'로만 사용하고 있다면 std::reference_wrapper 를 알아두면 좋다.
활용 예시
다음 예제코드는 std::reference_wrapper 활용하여 원하는 n의 값을 얻는 예제코드이다.
- main 함수에 n = 0 의 값을 foo 함수 내에서 처리된 10의 값을 얻고 싶다.
- 코드 실행해보기 (아래 우측 상단 아이콘 선택 시 실행 가능)
- 전체코드 한눈에 보기
#include <iostream>
#include <functional>
void foo(int& arg) { arg = 10; }
template<typename T>
void send_foo(T a)
{
foo(a);
}
int main()
{
int n = 0;
//1. 직접 참조 사용 시 문제 없음
foo(n);
std::cout << "foo() : " << n << std::endl;
n = 0;
//2-1. 전달 함수를 통해 사용 시
send_foo(n); // 받으면서 복사본 생성
std::cout << "send_foo() : " << n << std::endl;
//2-2. 포인터는 복사본은 아니지만 foo 대입 시 참조 변환 안됨 int* -> int (X) 컴파일 에러
// send_foo(&n);
//3-1. reference_wrapper : // n 을 참조로 전달하겠다는 의도.
std::reference_wrapper<int> r(n);
send_foo(r);
std::cout << "send_foo() : " << n << std::endl;
n = 0;
//3-2. std::ref(n) : reference_wrapper 를 만들어주는 함수
send_foo( std::ref(n) );
std::cout << "send_foo() : " << n << std::endl;
return 0;
}
결과:
foo() : 10
send_foo() : 0
send_foo() : 10
send_foo() : 10
case 1. 직접 참조의 경우는 10을 얻을 수 있다.
case 2. foo 함수가 send_foo 함수를 거쳐서 전달되는 경우
: send_foo 함수의 T a 의 값은 10이지만 a는 n의 참조 값이 아니기에 n은 10이 될 수 없다.
case 3. n 을 std::reference_wrapper로 전달하는 경우
: foo 에서 얻은 10의 값을 n이 가질수 있다. std::ref 는 reference_wrapper 를 만들어 주는 함수이다 내부적으로 3-1과 같이 std::reference_wrapper<int> r(n) 이 것이 수행된다.
C++ 표준 참조와 비교
C++ 표준 참조와 비교해 볼 수 있는 다음 예제코드를 통해 std::reference_wrapper 특성을 알아보자.
- v1, v2 를 참조하는 C++ 표준 참조 r1, r2 에서 r1 = r2 의 결과를 std::reference_wrapper 로 참조한 ref_r1, ref_r2의 값의 결과를 우선 비교해보자.
- 코드 실행해보기 (아래 우측 상단 아이콘 선택 시 실행 가능)
- 전체코드 한눈에 보기
#include <iostream>
#include <functional>
// C++ 레퍼런스 : 값의 이동, 레퍼런스 자체는 이동될수 없다
// reference_wrapper : 이동가능한 참조
// 대입연산시 참조가 이동
int main()
{
int v1 = 10, v2 = 20;
int& r1 = v1;
int& r2 = v2;
r1 = r2;
// raw reference
std::cout << "v1 : " << v1 << std::endl; // 20
std::cout << "v2 : " << v2 << std::endl; // 20
std::cout << "r1 : " << r1 << std::endl; // 20
std::cout << "r2 : " << r2 << std::endl; // 20
//init
v1 = 10, v2 = 20;
std::reference_wrapper<int> ref_r1 = v1;
std::reference_wrapper<int> ref_r2 = v2;
ref_r1 = ref_r2;
// raw reference reference_wrapper
std::cout << "v1 : " << v1 << std::endl; // 20 10
std::cout << "v2 : " << v2 << std::endl; // 20 20
std::cout << "ref_r1 : " << ref_r1 << std::endl; // 20 20
std::cout << "ref_r2 : " << ref_r2 << std::endl; // 20 20
}
//결과
v1 : 20
v2 : 20
r1 : 20
r2 : 20
v1 : 10
v2 : 20
ref_r1 : 20
ref_r2 : 20
위 코드에서 C++ 표준 참조는 r1는 v1을 참조하고, r2는 v2을 참조하기에 아래와 표현할 수 있다.
여기서 r1 = r2 을 하게되면 이 값을 복사하여 아래와 같이 v2, r2의 값 20이 r1, v1의 값이 20이 된다.
하지만, 위 코드에서 std::reference_wrapper는 ref_r1는 v1의 포인터를 참조하고, ref_r2는 v2의 포인터를 참조하고 있기에 ref_r1 = ref_r2를 하게 되면
아래와 같이 ref_r1 의 참조가 ref_r2로 바뀌기에 v1의 참조는 끊어지고, v2를 참조하여 v1을 제외하고 모두 20을 값을 가지게 된다.
위 예제코드를 통해 표준 C++ 참조와 특성을 비교해보면 다음과 같다.
C++ 참조 : 이동 불가능한 참조. "const" 이다. 대입연산시 '참조의 이동'이 아니라 '값의 이동'이다. (깊은 복사)
std::reference_wrapper : 참조가 변경이 가능한다. 대입 연산시 '참조의 이동'이 된다.
* 이 개념이 C++20의 "ref_view"로 발전
참고
std::reference_wrapper는 C++11 부터 지원되는 템플릿 클래스이며 <functional> 헤더에 정의되어 있다.
std::reference_wrapper 구현 내용을 보면, 객체를 가르키기위해서 참조 시 포인터를 가지고 있고, 기존 참조도 호환하기위해 operator T&() 가 있음을 확인 할 수 있다.
int& r3 = ref_r1; // 가능
https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
std::reference_wrapper - cppreference.com
template< class T > class reference_wrapper; (since C++11) 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::ve
en.cppreference.com
* 본 글에 예제 코드는 코드누리 교육을 받으면서 제공된 샘플코드를 활용하였습니다.
'L C++ > Concurrency' 카테고리의 다른 글
[Concurrency] 주요 기능 member type / class / functions 정리 (0) | 2023.08.01 |
---|---|
[Concurrency] 인자와 callable object (0) | 2023.07.24 |
[Concurrency] 좀비 스레드 생성될 수 있는가? (0) | 2023.07.18 |
[Concurrency] this_thread 특징 (0) | 2023.07.11 |
[Utilities] thread를 잘 사용하기 위한 std::reference_wrapper (std::ref) (활용 예시 추가) (0) | 2023.05.28 |