C++ 자료형 추론 (auto, decltype)

2021. 3. 10. 21:09·C++/Syntax

자료형 추론 (auto, decltype)

  • C++11

  • 자료형을 기본 타입 형태로 추론한다.

    • 자료형을 명시하기 복잡하거나 귀찮을 때 사용한다.

    • 가독성이 좋다.

  • 함수의 반환 값에도 적용할 수 있다.

    • Trailing Return Type(후행 반환 형식)

      • auto 키워드로 정의된 함수 반환 값의 자료형을 명시하는 것

      • 함수 앞에 자료형을 쓰는 것보다 가독성이 좋고, 인덴팅을 맞추기도 편하다.

      auto add(int x, int y)       -> int;
      auto add(double x, double y) -> double;

예제

iterator 대체

  • 개인적으로 이 때 제일 많이 사용하는 것 같다.

    #include <iostream>
    #include <vector>
    
    int        main()
    {
        using namespace std;
    
        vector<int> vec({ 1, 2, 3 });
    
        for (vector<int>::iterator itr = vec.begin(); itr != vec.end(); ++itr)
            cout << *itr;
        cout << endl;
        for (auto itr = vec.begin(); itr != vec.end(); ++itr)
            cout << *itr;
        cout << endl;
        for (const auto & itr : vec)
            cout << itr;
        cout << endl;
    }
    
    /* stdout
    123
    123
    123
    */

기본 자료형 대입

  • 작동 원리는 템플릿 타입 추론처럼 컴파일할 때 값을 복사해서 인스턴스화한다고 생각하면 될 것 같다.

    int        main()
    {
        using namespace std;
    
        int             x = int();
        auto            auto_x = x;         // int
        const int&      crx = x;
        auto            auto_crx1 = crx;    // int
        const auto&     auto_crx2 = crx;    // const int &
        volatile int    vx = 1024;
        auto            avx = vx;           // int
        volatile auto   vavx = vx;          // volatile int
    }
    • auto_crx1의 경우 원본인 crx가 const int& 자료형이므로 그 값을 R-value로 받아서 복사하는 것이다.

    • auto_crx2는 crx가 const int& 자료형이므로 값이 int형이고, 따라서 const int&와 같아진다.

    • avx의 경우에도 vx를 R-value처럼 값만 복사하여 int 자료형이 되는 것이다.


템플릿 타입 추론 (Template Type Deduction)

  • auto 키워드와 비슷하게 자료형을 추론해낸다.

    template <typename T>
    void    func(T arg)
    {}
    
    int        main()
    {
        const int& crx = 123;
    
        func(crx);              // void func<int>(int arg)
    }
    template <typename T>
    void    func(const T& arg)
    {}
    
    int        main()
    {
        const int& crx = 123;
    
        func(crx);              // void func<int>(const int& arg)
    }

레퍼런스

  • auto &의 형태로 사용하면 L-value 레퍼런스를 참조하는 것이기 때문에 const 등의 속성이 유지된다.

    int        main()
    {
        volatile int   c = 0;
        auto&       rc = c;     // const int &
    }

L-value, R-value References

  • auto &&의 형태로 사용하면 상황에 따라 달라진다.

    int        main()
    {
      int        i = 42;
      auto&& ri_1 = i;        // int &
      auto&& ri_2 = 42;        // int &&
    }

const 포인터

  • 포인터의 const * 속성은 유지된다.

    • 원본은 건드리면 안된다는 속성을 유지해야 하기 때문이다.
    int        main()
    {
      int            x = 42;
      const int*    p1 = &x;
      auto        p2 = p1;    // const int*
    }

자료형이 정해지지 않은 변수 사용 예제

  • 템플릿에 어떤 자료형이 들어오는가에 따라 변수의 자료형이 달라지는데, auto로 간단하게 처리할 수 있다.

    template <typename T, typename S>
    void    func(T lhs, S rhs)
    {
      auto    prod1 = lhs * rhs;
    
      typedef decltype(lhs* rhs)    product_type;
    
      product_type prod2 = lhs * rhs;
    
      decltype(lhs * rhs) prod3 = lhs * rhs;
    }
    • product_type은 auto 키워드가 등장하기 이전에 사용했던 방식이다.

    • decltype

      • 일부 컴파일러에서 관습적으로 사용하던 typeof를 공식적으로 내놓은 것이다.

자료형이 정해지지 않은 반환 값 정의 예제

  • auto와 decltype을 같이 사용하여 쉽게 해결할 수 있다.

    template <typename T, typename S>
    auto    func(T lhs, S rhs) -> decltype(lhs* rhs)
    {
      return lhs * rhs;
    }

auto와 decltype의 차이

  • decltype은 변수가 선언될 때의 자료형을 가져온다.

    • decltype 내부에 괄호가 감싸져있으면 L-value 레퍼런스로 가져온다.
    class S
    {
    public:
      int    x_;
    
      S()
      {
        x_ = 42;
      }
    };
    
    int        main()
    {
      int            x;
      const int    cx = 42;
      const int&    crx = x;
      const S*    p = new S();
    
      auto a = x;        // int
      auto b = cx;        // int
      auto c = crx;        // int
      auto d = p;        // const S*
      auto e = p->x_;    // int
    
      typedef decltype(x)            x_type;        // int
      typedef decltype(cx)        cx_type;        // const int
      typedef decltype(crx)        crx_type;        // const int&
      typedef decltype(p->x_)        x__type;    // int
    
      typedef decltype((x))        x_with_parens_type;            // int &
      typedef decltype((cx))        cx_with_parens_type;    // const int&
      typedef decltype((crx))        crx_with_parens_type;    // const int&
      typedef decltype((p->x_))    x__with_parens_type;        // const int&
    }
  • std::vector의 경우 [] 연산자 오버로딩이 L-value 레퍼런스를 반환하기 때문에 decltype이 레퍼런스로 잡힌다.

    #include <iostream>
    #include <vector>
    
    class S
    {
    public:
      int    x_;
    
      S()
      {
        x_ = 42;
      }
    };
    
    const S        foo()
    {
      return S();
    }
    
    const int&    foobar()
    {
      return 123;
    }
    
    int            main()
    {
      using namespace std;
    
      vector<int> vec = { 42, 43 };
    
      auto a = foo();                                // S
      typedef decltype(foo()) foo_type;                // const S
    
      auto b = foobar();                            // int
      typedef decltype(foobar()) foobar_type;        // const int&
    
      auto itr = vec.begin();                        // vector<int>::iterator
      typedef decltype(vec.begin()) iterator_type;    // vector<int>::iterator
    
      auto first_element = vec[0];                    // int
      decltype(vec[1]) second_element = vec[1];        // int&
    }
  • decltype에서 삼항연산자 등으로 값을 그대로 반환하게 될 경우 L-value 레퍼런스로 추론된다.

    int            main()
    {
      int x = 0;
      int y = 0;
      const int cx = 42;
      const int cy = 43;
      double d1 = 3.14;
      double d2 = 2.72;
    
      typedef decltype(x * y) prod_xy_type;    // int
      auto a = x * y;                            // int            
    
      typedef decltype(cx * cy) prod_xy_type;    // int
      auto a = cx * cy;                        // int
    
      typedef decltype(d1 < d2 ? d1 : d2) cond_type;    // double&
      auto c = d1 < d2 ? d1 : d2;                        // double
    
      typedef decltype(x < d2 ? x : d2) cond_type_mixed;    // double
      auto d = x < d2 ? x : d2;                            // double
    }
  • 위의 L-value 레퍼런스로 반환하는 특성때문에, std::remove_reference 구조체를 이용해야 할 때도 있다.

    • 반환 값이 L-value 레퍼런스일 때도 있고 R-value일 때도 있으면 헷갈리기 때문이다.
    #include <iostream>
    
    template <typename T, typename S>
    auto fpmin_wrong(T x, S y) -> decltype(x < y ? x : y)
    {
      return x < y ? x : y;
    }
    
    template <typename T, typename S>
    auto fpmin(T x, S y) ->
    typename std::remove_reference<decltype(x < y ? x : y)>::type
    {
      return x < y ? x : y;
    }
    
    int            main()
    {
      int        i = 42;
      double    d = 45.1;
      //auto    a = std::min(i, d); // 에러
      auto    a = std::min(static_cast<double>(i), d);
    
      int&    j = i;
    
      typedef decltype(fpmin_wrong(d, d)) fpmin_wrong_return_type1; // double&
      typedef decltype(fpmin_wrong(i, d)) fpmin_wrong_return_type2; // double
      typedef decltype(fpmin_wrong(j, d)) fpmin_wrong_return_type3; // double
    
      typedef decltype(fpmin(d, d)) fpmin_return_type1; // double
      typedef decltype(fpmin(i, d)) fpmin_return_type2; // double
      typedef decltype(fpmin(j, d)) fpmin_return_type3; // double
    }
  • decltype은 실제로 실행시키지 않고 추론만 하므로, 유효하지 않음에도 사용할 수 있는 경우가 있다.

    #include <vector>
    
    int            main()
    {
      std::vector<int> vec;
      typedef decltype(vec[0]) integer; // int&
    }
  • decltype은 nested type에 접근하기에도 유용하다.

    template <typename R>
    class SomeFunctor
    {
    public:
      typedef R result_type;
    };
    
    int            main()
    {
      SomeFunctor<int> func;
      typedef decltype(func)::result_type integer; // int
    }

lambda 함수에도 적용이 가능하다

  • 람다 함수 역시 decltype에서 소괄호로 한번 더 감싸주면 L-value 레퍼런스로 추론된다.

    #include <iostream>
    
    int            main()
    {
      using namespace std;
    
      auto lambda = []() { return 42; };
      decltype(lambda) lambda2(lambda);
      decltype((lambda)) lambda3(lambda);
    
      cout << &lambda << '\n' << &lambda2 << '\n' << &lambda3 << endl;
    }
    
    /* stdout
    0053F717
    0053F70B
    0053F717
    */
  • 람다 함수의 파라미터에는 auto 키워드가 올 수 있다.

    #include <iostream>
    
    int            main()
    {
      using namespace std;
    
      auto lambda = [](auto x, auto y)
      {
        return x + y;
      };
    
      cout << lambda(1.1, 2) << endl;
      cout << lambda(3, 4) << endl;
      cout << lambda(4.5, 2.2) << endl;
    }
    
    /* stdout
    3.1
    7
    6.7
    */
저작자표시 (새창열림)

'C++ > Syntax' 카테고리의 다른 글

C++ 열거형 (Enumerate Type)  (0) 2021.03.10
C++ 형변환 (Type Conversion)  (0) 2021.03.10
C++ using  (0) 2021.03.10
C++ extern  (0) 2021.03.10
C++ 범위 지정 연산자 (Scope Resolution Operator)  (0) 2021.03.10
'C++/Syntax' 카테고리의 다른 글
  • C++ 열거형 (Enumerate Type)
  • C++ 형변환 (Type Conversion)
  • C++ using
  • C++ extern
Caniro
Caniro
  • Caniro
    Minimalism
    Caniro
  • 전체
    오늘
    어제
    • 분류 전체보기 (317)
      • Algorithm (13)
        • 알기 쉬운 알고리즘 (10)
        • Search (1)
        • Sort (2)
      • Arduino (0)
      • C++ (185)
        • Class (46)
        • Exception (6)
        • Library (51)
        • Overloading (10)
        • SmartPointer (5)
        • Syntax (33)
        • TBC++ (23)
        • Templates (9)
        • VisualStudio (2)
      • Embedded (1)
      • Git (4)
      • Java (5)
      • Linux (16)
        • Error (1)
        • Linux Structure (11)
      • MacOS (7)
      • OS (1)
        • Concurrency (1)
      • Python (21)
        • Class (1)
        • Function (2)
        • Syntax (17)
      • Raspberrypi (9)
      • Review (1)
      • Utility (12)
        • VSCode (5)
        • VirtualBox (3)
      • Web (8)
        • Nginx (1)
        • React (3)
        • Django (1)
      • Windows (20)
        • Registry (3)
        • WSL (1)
        • DeviceDriver (6)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    KakaoTalk
    Workspace
    SFC
    윈도우
    제외
    EXCLUDE
    시스템 복구
    알림
    스프링
    mspaint
    그림판
    citrix workspace
    MacOS
    Windows 11
    윈도우 명령어
    로지텍 마우스 제스처
    dism
    SunOS 5.1
    vscode
    windows
    spring
    스프링 프레임워크 핵심 기술
    Solaris 10
    맥북 카카오톡 알림 안뜸
    unix
    logi options
    백기선
    java
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Caniro
C++ 자료형 추론 (auto, decltype)
상단으로

티스토리툴바