본문으로 바로가기

C++11) std::reference_wrapper

category C++/Modern 2019. 2. 14. 03:00

<개요>


C++11에 추가된 참조를 래핑하는 클래스 std::reference_wrapper 와 도우미 역할인 std::ref, std::cref에 대해 작성합니다. 


<std::reference_wrapper>


std::reference_wrapper는 다음과 같이 작성돼 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
template <class Ty>
class reference_wrapper
{
public:
    typedef Ty type;
 
    reference_wrapper(Ty&) noexcept;
    operator Ty&() const noexcept;
    Ty& get() const noexcept;
 
    template <class... Types>
    auto operator()(Types&&... args) const ->
        decltype(std::invoke(get(), std::forward<Types>(args)...));
 
private:
    Ty *ptr; // 오직 ptr만 가지고 있음
};
cs


reference_wrapper<Ty> 는 Ty 형식의 객체 또는 함수에 대한 참조를 래핑하며 해당 형식의 개체를 가르키는 포인터가 포함된 복사본/할당 가능 래퍼입니다.
reference_wrapper 를 사용하면 표준 컨테이너에 참조를 저장하고, std::bind에 대한 참조를 통해 객체를 전달할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <functional>
#include <iostream>
 
int main() {
    int i = 1;
    std::reference_wrapper<int> rwi(i);
 
    std::cout << "i = " << i << std::endl;
    std::cout << "rwi = " << rwi << std::endl;
    rwi.get() = -1;
    std::cout << "i = " << i << std::endl;
 
    return (0);
}
cs

get을 통해 래핑된 참조를 가져옵니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <functional>
using namespace std;
void printNumber(int i) {
    cout << i << endl;
}
int main() {
    int n = 4;
    std::function<void()> print1 = std::bind(&printNumber, n);
    std::function<void()> print2 = std::bind(&printNumber, std::ref(n));
 
    n = 5;
 
    print1();    // 4 를 출력
    print2();    // 5 를 출력
}
cs

std::ref 는 대부분 std::bind와 결합되어 사용됩니다. 위의 상황에서 n 이 5로 증가했을때 print2만 바뀐 이유는 n을 std::ref로 감쌌기 때문입니다.
내부에선 ptr이 n을 가리키고 있는 상태라고 생각하시면 쉽습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <functional>
#include <iostream>
 
template <typename T>
void func(T _t) {
    _t++;
}
int main() {
    int i = 1;
    std::cout << i << std::endl;    // 1
    func(i);
    std::cout << i << std::endl;    // 1
    func(std::ref(i));
    std::cout << i << std::endl;    // 2
}
cs

 T의 타입은 int 입니다. 그러나 ++는 std::ref 의 타겟의 참조가 될 _t.operator int&() 에 반영됩니다.
왜냐면 operator Ty&() 때문에 std::reference_wrapper<int>에서 int& 로의 암시적 변환이 일어나기 때문입니다.

사실 작성해보니 예시가 조금 적은 것 같은데, 생각나면 더 올리겠음.