프로그래밍 언어/C & C++ 정리

decltype과 템플릿

뽀또치즈맛 2024. 11. 27. 01:29

 
 

decltype의 개요

decltype 형식 지정자는 지정된 식의 형식을 생성한다.
즉 쉽게 말해 받아온 개체 타입을 그대로 개체를 생성해주는 키워드이다.

 

decltype의 일반적 용례 템플릿

decltype 형식 지정자는 auto 키워드와
함께 템플릿 라이브러리를 작성하는 개발자에게 주로 유용하다.
 

decltype 및 auto

C++14에서는 후행 반환 형식 없이 decltype(auto)를 사용하여
반환 형식이 템플릿 인수에 따라 달라지는함수 템플릿을 선언할 수 있다.
 
C++11에서는 auto 키워드와 함께 후행 반환 형식에 대한
decltype 형식 지정자를 사용하여 반환 형식이
해당 템플릿 인수의 형식에 종속되는 함수 템플릿을 선언할 수 있다.
 
예를 들어, 함수 템플릿의 반환 형식이
템플릿 인수의 형식에 종속되는 것을 보여주는
다음 예제 코드를 살펴보면, 
코드의 에에서 UNKNOWN 자리 표시자는 반환 형식을 지정할 수 없음을 나타낸다.

template<typename T, typename U>
UNKNOWN func(T&& t, U&& u) { return t + u; };

 
 
decltype 형식 지정자의 도입으로 개발자는 함수 템플릿이
반환하는 식의 형식을 가져올 수 있게 되었다.
컴파일하면 지정되는 반환 형식을 선언하려면,
(컴파일 타임에 다시 살펴봐야 하는) 대체 함수 선언 구문에
auto 키워드 및 decltype 형식 지정자를 사용하며 된다.
(컴파일 타임에 지정되는)반환 형식은 코딩 할 때가 아니라
선언이 컴파일 될 때 결정된다.
 
다음 아래 코드 프로토타입에서 대체 함수 선언의 구문을 보았을 때,
const 및 volatile 한정자와 throw 예외 사항은 선택 사항이다.
함수 구현부(function_body) 자리 표시자는 함수가 수행하는
작업을 지정하는 복합문을 나타낸다.
좋은 모범 코딩 예는 decltype 문의 expression 자리 표시자를
함수 구현부의 return 문(return 문이 있는 경우)에 의해
지정된 식과 일치시키는 것이다.
 

auto function_name ( parametersopt ) 
constopt volatileopt -> decltype( expression ) noexceptopt { function_body };

 
 
아래 예제 코드에서 myFunc 함수 템플릿의 컴파일하면
지정되는 반환 형식이 t 및 u 템플릿 인수의 형식에 따라 결정된다.
모범 코딩 사례에 따라 코드 예제는 rvalue 참조 및 forward 함수 템플릿도 사용한다.

//C++11
// std::forward
// 문맥에 따라 & L-value Reference, && R-value Reference 을
// 구분하지 못하는 경우 std::forward를 통해 타입을 분명하게 해준다. 
template<typename T, typename U>
auto myFunc(T&& t, U&& u) -> decltype (forward<T>(t) + forward<U>(u))
        { return forward<T>(t) + forward<U>(u); };

//C++14
template<typename T, typename U>
decltype(auto) myFunc(T&& t, U&& u)
        { return forward<T>(t) + forward<U>(u); };

 
 

decltype 및 전달 함수 

 
전달 함수는 다른 함수에 대한 호출을 래핑한다.
해당 인수 또는 이러한 인수를 포함하는 식의 결과를
다른 함수로 전달하는 템플릿을 고려해보자.
또한 전달 함수는 다른 함수를 호출한 결과를 반환해야 한다.
 
이러한 점을 미루어 보아,
함수의 반환 형식은 래핑된 함수의 반환 형식과 동일해야 한다.
 
따라서 decltype 형식 지정자 없이는 적절한 형식의 함수를 만들기 힘들다.
decltype 형식 지정자는 함수가
참조 형식을 반환하는지 여부에 대한 필수 정보를 잃지 않기 때문에
제네릭 전달 함수를 사용할 수 있게 만든다.
 
( 전달 함수의 이해가 미흡하다면
이전 예시 코드인 myFunc의 예시를 참고하면 도움이 될 것이다. )
 

해당 내용 예제
 

다음 코드 예에서 함수 템플릿 Plus()의 늦게 지정된 반환 형식을 선언한다.
Plus 함수는 해당 두 피연산자를 operator+ 오버로드로 처리한다.
따라서 Plus 함수의 반환 형식 및 더하기 연산자 + 에 대한 해석은
함수 인수의 형식에 따라 달라진다.
 

#include <iostream>
#include <string>
#include <utility>
#include <iomanip>

using namespace std;

template < typename T1, typename T2>
auto Plus(T1&& t1, T2&& t2) -> decltype(forward<T1>(t1) + forward<T2>(t2))
{
    return forward<T1>(t1) + forward<T2>(t2);
}

class X
{
    friend X operator+(const X& x1, const X& x2) {
        return X(x1.m_data + x2.m_data);
    }

public:
    X(int data) : m_data(data) {}
    int Dump() const { return m_data; }

private:
    int m_data;
};

int main() {
    int i = 4;
    cout << "Plus(i(4) , 9) = " << Plus(i, 9) << endl;

    float dx = 4.f, dy = 9.5f;
    // setprecision 함수는 소수점 출력 범위를 지정할 때 쓴다.
    cout << setprecision(3) << "Plus(dx(4.f) , dy(9.5f)) = " << endl;

    string hello = "Hello, ";
    string world = "world!";
    cout << "Plus Function used String  :  " << Plus(hello, world) << endl;

    X x1(20);
    X x2(22);
    X x3 = Plus(x1, x2);

    cout << "x3.Dump() = " << x3.Dump() << endl;


    return 0;
}

 
실행 결과

 
 
++PLUS++

 std::forward는
r-value가 들어오면 r-value로 자료형을 확정 짓고,
l-value가 들어오면 l-value로 자료형을 확정 짓는다.
 

이러한 것을 퍼펙트 포워딩이라고 부른다.


++++++++
 
참고 사이트
https://learn.microsoft.com/ko-kr/cpp/cpp/decltype-cpp?view=msvc-170

 

decltype(C++)

자세히 알아보기: decltype(C++)

learn.microsoft.com

https://ansohxxn.github.io/cpp/chapter19-7/

 

C++ Chapter 19.7 : 완벽한 전달과 std::forward

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀 🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!

ansohxxn.github.io