###### tags: `C++` # Magic std::make_pair ## use `std::make_pair` in a common method I want to use `std::make_pair` to initilize a `std::pair` object, so I use the following code. ``` C++= int main() { int n=5; std::pair<int, int> p = std::make_pair<int,int>(n,n); } ``` However, the compilation is failed and I got the message: ``` <source>:23:53: error: cannot bind rvalue reference of type 'int&&' to lvalue of type 'int' 23 | std::pair<int, int> p = std::make_pair<int,int>(n,n); | ^ In file included from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/stl_algobase.h:64, from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/char_traits.h:39, from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/ios:40, from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/ostream:38, from /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/iostream:39, from <source>:1: /opt/compiler-explorer/gcc-9.2.0/include/c++/9.2.0/bits/stl_pair.h:524:21: note: initializing argument 1 of 'constexpr std::pair<typename std::__decay_and_strip<_Tp>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&) [with _T1 = int; _T2 = int; typename std::__decay_and_strip<_T2>::__type = int; typename std::__decay_and_strip<_Tp>::__type = int]' 524 | make_pair(_T1&& __x, _T2&& __y) ``` It means that we attempt to bind a lvalue `n` to a rvalue reference `__x`. Therefore, I read the std::make_pair in [cppreference](https://en.cppreference.com/w/cpp/utility/pair/make_pair) and I see the definition of the make_pair. ``` cpp template< class T1, class T2 > constexpr std::pair<V1, V2> make_pair( T1&& t, T2&& u ); ``` where The deduced types V1 and V2 are std::decay\<T1>::type and std::decay\<T2>::type (the usual type transformations applied to arguments of functions passed by value) unless application of std::decay results in std::reference_wrapper\<X> for some type X, in which case the deduced type is X&. Therefore, the signature of make_pair is related of the argument's type. Let's see if we specify the int to `std::make_pair`, what do we see for the argument. ``` std::make_pair<int,int> => constexpr std::pair<std::decay<int>::type, std::decay<int>::type> std::make_pair(int && t, int&& u); => constexpr std::pair<int, int> std::make_pair(int&& t, int&& u); ``` It's why compiler tell as n cannot bind to rvalue reference because t is an rvalue reference argument. After understanding the deduced rule, we have several methods to generate the std::pair<int,int>. ``` cpp= int main() { int n = 42; // method 1, don't specify the type in the function std::pair<int,int> a = std::make_pair(n,n); std::pair<int,int> b = std::make_pair(1,2); // method 2, specify the correct type // 2-1: n is a lvalue reference std::pair<int,int> c = std::make_pair<int&, int&>(n,n); // 2-2: change n to rvalue reference std::pair<int,int> d = std::make_pair<int, int>(std::move(n),std::move(n)); // 2-3: use a temperatory value std::pair<int,int> e = std::make_pair<int, int>(1,2); } ``` ## use the reference in the pair We know how to use `std::make_pair` correctly now. However, how do we create a pair containing the reference. The following is a possible method. ``` cpp= int main() { int n = 42; std::pair<int, int&> a = std::make_pair(1, n); } // error message: <source>:23:44: error: conversion from 'pair<[...],int>' to non-scalar type 'pair<[...],int&>' requested 23 | std::pair<int, int&> a = std::make_pair(1, n); | ~~~~~~~~~~~~~~^~~~~~ ``` Go back and see the usage of the make_pair ``` cpp template< class T1, class T2 > constexpr std::pair<V1, V2> make_pair( T1&& t, T2&& u ); ``` and `V1` is `std::decay<T1>::type` so that `std::make_pair(1,n)` should return `std::pair<int,int>` but not `std::pair<int, int&>` So, how to use std::make_pair to create a pair containing a reference? We can refer to `std::ref` and `std::reference_wrapper` in STL. ```c++= int main(){ int n = 42; std::pair<int, int&> a = std::make_pair(1, std::ref(n)); std::pair<int, int&> b = std::make_pair<int, std::reference_wrapper<int>>(1, n); } ``` In line 3, `std::ref(n)` specify `T2` as `std::reference_wrapper\<int>` and `std::decay<T2>::type` is a `int&`. line 4 is same as line 3, we specify the `T2` explicitly. # Take Away Create the std::pair with std::make_pair ``` cpp= // without any reference in the pair int main() { int n = 42; // method 1, don't specify the type in the function std::pair<int,int> a = std::make_pair(n,n); std::pair<int,int> b = std::make_pair(1,2); // method 2, specify the correct type // 2-1: n is a lvalue reference std::pair<int,int> c = std::make_pair<int&, int&>(n,n); // 2-2: change n to rvalue reference std::pair<int,int> d = std::make_pair<int, int>(std::move(n),std::move(n)); // 2-3: use a temperatory value std::pair<int,int> e = std::make_pair<int, int>(1,2); } // with reference in the pair int main(){ int n = 42; std::pair<int, int&> a = std::make_pair(1, std::ref(n)); std::pair<int, int&> b = std::make_pair<int, std::reference_wrapper<int>>(1, n); } ```