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

2021. 3. 10. 21:09·SW개발/C++
목차
  1. 예제
  2. iterator 대체
  3. 기본 자료형 대입
  4. 템플릿 타입 추론 (Template Type Deduction)
  5. 레퍼런스
  6. L-value, R-value References
  7. const 포인터
  8. 자료형이 정해지지 않은 변수 사용 예제
  9. 자료형이 정해지지 않은 반환 값 정의 예제
  10. auto와 decltype의 차이
  11. lambda 함수에도 적용이 가능하다
반응형

자료형 추론 (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
    */
반응형
저작자표시 (새창열림)

'SW개발 > C++' 카테고리의 다른 글

C++ 형변환 (Type Conversion)  (0) 2021.03.10
C++ typeinfo  (0) 2021.03.10
C++ using  (0) 2021.03.10
C++ extern  (0) 2021.03.10
따라하며 배우는 C++ 4장  (0) 2021.03.10
  1. 예제
  2. iterator 대체
  3. 기본 자료형 대입
  4. 템플릿 타입 추론 (Template Type Deduction)
  5. 레퍼런스
  6. L-value, R-value References
  7. const 포인터
  8. 자료형이 정해지지 않은 변수 사용 예제
  9. 자료형이 정해지지 않은 반환 값 정의 예제
  10. auto와 decltype의 차이
  11. lambda 함수에도 적용이 가능하다
'SW개발/C++' 카테고리의 다른 글
  • C++ 형변환 (Type Conversion)
  • C++ typeinfo
  • C++ using
  • C++ extern
Caniro
Caniro
  • Caniro
    Minimalism
    Caniro
  • 전체
    오늘
    어제
    • 전체보기 (319)
      • SW개발 (268)
        • Java Spring (6)
        • C++ (186)
        • Python (21)
        • Linux (16)
        • 알고리즘 (13)
        • Git (4)
        • Embedded (1)
        • Raspberrypi (9)
        • React (3)
        • Web (2)
        • Windows Device Driver (6)
      • IT(개발아님) (46)
        • Windows (26)
        • MacOS (7)
        • Utility (11)
      • 챗봇 짬통 (0)
      • 일상 (2)
  • 블로그 메뉴

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

  • 공지사항

  • 인기 글

  • 태그

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

  • 최근 글

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

개인정보

  • 티스토리 홈
  • 포럼
  • 로그인
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.