解析C++11的std::ref、std::cref源碼
1、源碼準備
本文是基於gcc-4.9.0的源代碼進行分析,std::ref和std::cref是C++11才加入標準的,所以低版本的gcc源碼是沒有這兩個的,建議選擇4.9.0或更新的版本去學習,不同版本的gcc源碼差異應該不小,但是原理和設計思想的一樣的,下面給出源碼下載地址
http://ftp.gnu.org/gnu/gcc
2、std::ref和std::cref的作用
C++本身就有引用(&),那為什麼C++11又引入瞭std::ref(或者std::cref)呢?
主要是考慮函數式編程(如std::bind)在使用時,是對參數直接拷貝,而不是引用。下面是一個簡單的例子:
#include <functional> #include <iostream> void fun(int& n1, int& n2, const int& n3) { std::cout << "In function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; ++n1; // increments the copy of n1 stored in the function object ++n2; // increments the main()'s n2 // ++n3; // compile error std::cout << "In function end: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; } int main() { int n1 = 1, n2 = 1, n3 = 1; std::function<void()> fff = std::bind(f, n1, std::ref(n2), std::cref(n3)); std::cout << "Before function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; fff(); std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '\n'; }
運行結果:
Before function: 1 1 1
In function: 1 1 1
In function end: 2 2 1
After function: 1 2 1
從上面的例子中可以看到,執行完fff,n1的值仍然是1,n2的值已經改變,這說明std::bind使用的是參數的拷貝而不是引用,這也就是為什麼C++11要引入std::ref和std::cref的原因瞭,接下來分析std::ref的實現(std::cref不作分析,因為和std::ref的位移差別隻是引用變成瞭const而已)
3、std::ref相關源碼解析
3.1、std::ref解析
std::ref位於libstdc++-v3\include\std\functional中
template<typename _Tp> inline reference_wrapper<_Tp> ref(_Tp& __t) noexcept { return reference_wrapper<_Tp>(__t); } template<typename _Tp> void ref(const _Tp&&) = delete; template<typename _Tp> inline reference_wrapper<_Tp> ref(reference_wrapper<_Tp> __t) noexcept { return ref(__t.get()); }
從源代碼中可以看出:
- std::ref是一個模板函數,返回值是模板類
std::reference_wrapper
- 從第二個函數可以看到,std::ref不允許傳遞右值引用參數,即無法包裝右值引用傳遞的值
- std::ref的傳入參數可以是一個普通的引用,也可以是另外一個
std::reference_wrapper
對象,接下來分析std::reference_wrapper
的實現
3.2、std::reference_wrapper解析
std::reference_wrapper位於libstdc++-v3\include\std\functional中
template<typename _Tp> class reference_wrapper : public _Reference_wrapper_base<typename remove_cv<_Tp>::type> { _Tp* _M_data; public: typedef _Tp type; reference_wrapper(_Tp& __indata) noexcept :_M_data(std::__addressof(__indata)) { } reference_wrapper(_Tp&&) = delete; reference_wrapper(const reference_wrapper<_Tp>& __inref) noexcept :_M_data(__inref._M_data) { } reference_wrapper& operator=(const reference_wrapper<_Tp>& __inref) noexcept { _M_data = __inref._M_data; return *this; } operator _Tp&() const noexcept { return this->get(); } _Tp& get() const noexcept { return *_M_data; } template<typename... _Args> typename result_of<_Tp&(_Args&&...)>::type operator()(_Args&&... __args) const { return __invoke(get(), std::forward<_Args>(__args)...); } };
從源代碼中可以獲得以下信息:
- 該類繼承於
std::_Reference_wrapper_base
- 有一個類成員
_M_data
,類型為所引用類型的指針 - 第一個構造函數通過調用
std::__addressof
函數,獲得瞭指向引用參數的指針,並賦值給瞭_M_data(這也是為什麼不支持右值引用的原因,因為取不到對應的地址),std::__addressof
實現如下:
// 位於**libstdc++-v3\include\bits\move.h**中 // 借助reinterpret_cast能任意轉換類型的特性來將<code>_Tp&</code>轉為<code>_Tp*</code> //(轉換過程編譯器不保證正確,要由程序員來保證轉換過程不出錯,雖然標準庫用瞭很多這樣的特殊技巧,但是實際開發中這些少用為好) template<typename _Tp> inline _Tp* __addressof(_Tp& __r) _GLIBCXX_NOEXCEPT { return reinterpret_cast<_Tp*>(&const_cast<char&>(reinterpret_cast<const volatile char&>(__r))); }
- 拷貝構造函數和賦值函數就隻是簡單地將_M_data的值進行傳遞而已瞭
- 其餘方法就是為瞭讓std::reference_wrapper展現出和普通的引用一樣的效果而進行的運算符重載啥的,這裡就不贅述瞭,實現比較簡單,大傢可以自己看一看具體的代碼
3.3、std::remove_cv解析
std::remove_cv位於libstdc+±v3\include\std\type_traits中
分析std::_Reference_wrapper_base之前先看一下std::remove_cv的實現
其實從std::remove_cv存在於type_traits文件這一點就可以大致推斷出,std::remove_cv使用瞭模板元技術,模板元的主要思想為:利用模板特化機制實現編譯期條件選擇結構,利用遞歸模板實現編譯期循環結構,模板元程序則由編譯器在編譯器解釋運行,但是其也有明顯的優缺點,優點是運行時速度極快,缺點是程序很難看懂,容易勸退初學者,這裡不對其做深入分析,知道是這樣一個東西就行,有興趣的可以去查閱專業的C++書籍去瞭解其中的奧秘
源代碼如下,作用是將模板_Tp的const和voaltile屬性分離,這樣的話使用::value就可以得到沒有const、volatile的類型瞭
/// remove_const template<typename _Tp> struct remove_const { typedef _Tp type; }; template<typename _Tp> struct remove_const<_Tp const> { typedef _Tp type; }; /// remove_volatile template<typename _Tp> struct remove_volatile { typedef _Tp type; }; template<typename _Tp> struct remove_volatile<_Tp volatile> { typedef _Tp type; }; /// remove_cv template<typename _Tp> struct remove_cv { typedef typename remove_const<typename remove_volatile<_Tp>::type>::type type; };
3.4、std::_Reference_wrapper_base解析
std::_Reference_wrapper_base位於libstdc++-v3\include\std\functional中
template<typename _Tp> struct _Reference_wrapper_base :_Reference_wrapper_base_impl< __has_argument_type<_Tp>::value, __has_first_argument_type<_Tp>::value && __has_second_argument_type<_Tp>::value, _Tp> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res(_T1)> : unary_function<_T1, _Res> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res(_T1) const> : unary_function<_T1, _Res> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res(_T1) volatile> : unary_function<_T1, _Res> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res(_T1) const volatile> : unary_function<_T1, _Res> {}; // - a function type (binary) template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res(_T1, _T2)> : binary_function<_T1, _T2, _Res> {}; template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res(_T1, _T2) const> : binary_function<_T1, _T2, _Res> {}; template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res(_T1, _T2) volatile> : binary_function<_T1, _T2, _Res> {}; template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res(_T1, _T2) const volatile> : binary_function<_T1, _T2, _Res> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res(*)(_T1)> : unary_function<_T1, _Res> {}; template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res(*)(_T1, _T2)> : binary_function<_T1, _T2, _Res> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res (_T1::*)()> : unary_function<_T1*, _Res> {}; template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res (_T1::*)(_T2)> : binary_function<_T1*, _T2, _Res> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res (_T1::*)() const> : unary_function<const _T1*, _Res> {}; template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res (_T1::*)(_T2) const> : binary_function<const _T1*, _T2, _Res> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res (_T1::*)() volatile> : unary_function<volatile _T1*, _Res> {}; template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res (_T1::*)(_T2) volatile> : binary_function<volatile _T1*, _T2, _Res> {}; template<typename _Res, typename _T1> struct _Reference_wrapper_base<_Res (_T1::*)() const volatile> : unary_function<const volatile _T1*, _Res> {}; template<typename _Res, typename _T1, typename _T2> struct _Reference_wrapper_base<_Res (_T1::*)(_T2) const volatile> : binary_function<const volatile _T1*, _T2, _Res> {};
從代碼中可以看出,std::_Reference_wrapper_base繼承於std::unary_function或者std::binary_function,在實際編程中對std::reference_wrapper的作用不大,除非引用的是一個函數對象,所以在這裡就不分析它的具體作用瞭,大傢直接去查一下unary_function和binary_function是啥東西就行瞭
4、總結
std::ref和std::cref在函數式編程中的作用是非常大的,C++11後的源代碼中多次使用到瞭它們。而std::ref和std::cref事實上是模板函數,返回值是一個std::reference_wrapper對象,而std::reference_wrapper雖然是一個對象,可是他卻能展現出和普通引用類似的效果,這點和前一篇文章講的智能指針如出一轍(事實上標準庫大多是這樣設計的,這也是運算符重載存在的一個重要意義)。當我們在函數式編程(如std::bind)中需要對參數進行引用傳遞時,隻需要用std::ref或std::cref修飾該引用即可
到此這篇關於解析C++11的std::ref、std::cref源碼的文章就介紹到這瞭,更多相關C++11 std::ref、std::cref 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 淺談C++11的std::function源碼解析
- 一文搞懂c++中的std::move函數
- C++11中的智能指針shared_ptr、weak_ptr源碼解析
- 詳解C++右值引用
- 一篇文章弄懂C++左值引用和右值引用