해석 C++11의 std::ref,std::cref 원본

10875 단어 C++11std::refstd::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 << '
'; ++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 << '
'; } 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 << '
'; fff(); std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '
'; }
실행 결과:
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_데이터(이것도 오른쪽 값 인용을 지원하지 않는 이유입니다. 대응하는 주소를 찾을 수 없기 때문입니다), 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_데이터의 값을 전달할 뿐이다
  • 나머지 방법은 std::reference_래퍼가 일반적인 인용과 같은 효과를 보여 주는 연산자 재부팅 등은 군더더기 없이 간단합니다. 구체적인 코드를 직접 보실 수 있습니다.
  • 3.3、std::remove_cv 분석


    std::remove_cv는libstdc+±v3\include\std\type_에 있습니다traits 중
    분석 std::_Reference_wrapper_베이스 전에 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_래퍼는 비록 하나의 대상이지만 일반 인용과 유사한 효과를 나타낼 수 있다는 점은 이전 글에서 말한 스마트 바늘과 똑같다(사실 표준 라이브러리는 대부분 이렇게 설계되었는데 이것은 연산자가 다시 존재하는 중요한 의미이기도 하다).함수식 프로그래밍 (예: std::bind) 에서 매개 변수를 인용하여 전달해야 할 때, std::ref 또는 std::cref로 이 인용을 수식하면 된다
    C++11을 해석하는 std::ref,std::cref 원본 코드에 대한 이 글은 여기에 소개되었습니다. 더 많은 관련 C++11std::ref,std::cref 내용은 저희 이전의 글을 검색하거나 아래의 관련 글을 계속 훑어보십시오. 앞으로 많은 응원 부탁드립니다!

    좋은 웹페이지 즐겨찾기