[Advanced C++] 23. auto, 함수 반환형에서의 auto, 함수 매개변수에서의 auto, decltype(), Trailing Return Type (후행 반환 타입)

dev.kelvin·2025년 2월 27일
1

Advanced C++

목록 보기
23/74
post-thumbnail

1. auto

auto

C++에서는 모든 객체에 대해 명시적인 타입이 제공되어야 한다

	float f{ 10.f };

위 코드는 float타입의 변수 f를 float타입의 literal 값인 10.0으로 초기화 한 코드이다

결국 float이라는 동일한 타입 정보를 두 번 제공하는 셈이다

C++에서 타입 추론(Type Deduction)은 컴파일러가 초기화값을 기반으로 해당 객체의 타입을 추론하는걸 의미한다

이때 타입을 auto 키워드로 사용한다

	auto f{ 10.f }; //f는 float로 타입 추론이 된다
    auto i{ 1 + 3 }; //i는 int로 타입 추론이 된다

C++17 이전에는 auto d{ 10.0 }은 double이 아닌 std::initializer_list< double >로 추론되었지만 수정되었다

C++14 이하 버전에서 auto를 사용할 시 { }가 아닌 =으로 복사초기화를 해야 한다

물론 변수의 타입에만 사용할 수 있는게 아니라 함수의 반환형에도 사용이 가능하다

	auto add(int a, int b)
    {
    	return a + b;
    }
    //add()의 반환형은 int로 추론된다

auto를 사용할 때 literal suffix를 사용하여 특정 타입을 명확하게 지정할 수 있다
ex) f, u

또한 auto를 사용할 때 const, constexpr도 사용이 가능하다

	const auto a{ 10 };
    constexpr auto b{ 10.f };

auto를 사용하기 위해서는 반드시 초기화값이 존재해야 한다

	void foo()
    {
    }
    
	auto a;
    auto a{ };
    auto a{ foo() };
    
    //위의 3케이스 전부 사용 불가

auto 변수에 const값을 할당하게 되면 const는 제거된다, 따라서 명시적으로 auto에 const를 붙혀줘야한다

	const int i{ 10 };
    auto a{ i }; //const가 아님
    
    const auto a{ i }; //이렇게 const를 명시해야함

단 C-Style의 문자열로 초기화 시 객체의 타입이 const char*이기 때문에 auto타입 변수에 초기화했을때 string이 나오지 않는다

	auto s{ "Kelvin" }; //auto는 const char* 타입으로 추론된다

std::string이나 std::string_view 타입으로 추론 시키려면 s, sv suffix를 사용해야 한다

	#include <string>
	#include <string_view>
    
	using namespace std::literals; //s, sv suffix 사용을 위해 필요한 namespace
	auto s{ "Kelvin"s }; //std::string으로 추론
    auto s{ "Kelvin"sv }; //std::string_view로 추론

constexpr은 타입 시스템이 아니다, 단순히 값이 컴파일타임에 계산된다는걸 보장해주는 키워드이다
따라서 auto의 타입 추론 시스템에서 유지되지 않는다 (const로 유지됨)

	constexpr double a{ 10.5 };
    
    auto temp{ a }; //a
    const auto temp{ a }; //const
    constexpr auto temp{ a }; //constexpr

auto의 장단점

장점

  • 우선 코드가 더 정렬되어 보인다 (각각 다른 타입 대신 auto를 사용하면 정렬되어 보임, 하지만 장점이라고 해야할까..?)

  • 초기화가 강제되어 UnInitialized 이슈를 방지할 수 있다

  • 불필요한 타입변환이 방지된다

	#include <string_view>
    
    std::string_view foo()
    {
    	
    }
    
    auto s{ foo() }; //따로 불필요한 타입변환 없이 바로 std::string_view 타입으로 추론된다

단점

  • 타입이 명확하지 않아 프로그래머가 변수만 보고 타입파악이 힘들다
    따라서 cast<>에 타입이 작성되는것 처럼 명시적으로 타입이 확인되는 경우에만 auto를 사용하자
    (아니면 iterator처럼 아주 복잡한 타입에만?)

2. 함수에서의 auto

함수에서의 타입 추론

	int add(int a, int b)
    {
    	return a + b;
    }

위 함수가 컴파일될 때 컴파일러는 a + b가 int라는것을 결정하고 반환값의 타입이 함수 정의에 작성한 반환형과 일치하는지 혹은 타입변환이 가능한지 확인한다

C++14부터 auto를 함수의 반환형에 사용하여 타입추론이 가능해졌다

	auto add(int a, int b)
    {
    	return a + b;
    }

컴파일러가 return을 분석하여 반환형을 결정하여 타입추론을 한다 (위 예시에서는 int로 추론)

함수에서 auto를 사용할때는 return타입이 전부 같아야한다

	auto foo(bool b)
    {
    	if (b)
        {
        	return 5;
        }
        else
        {
        	return 5.5f;
        }
    }
    
    //compile error

반환형이 복잡하거나 예상하기 힘든경우 auto를 사용하면 간결한 함수를 만들 수 있다 (마찬가지로 프로그래머가 타입을 명확하게 인지하기 힘들다 따라서 반환형을 auto가 아닌 명확히 작성하는걸 권장한다)

반환형이 auto인 함수는 전방선언만으로는 사용이 불가능하다, 정의가 해당파일에 존재해야 한다

	auto add(int, int);

    int main()
    {
        add(10, 20);

        return 0;
    }

    auto add(int a, int b)
    {
        return a + b;
    }
	
    //compile error

따라서 헤더파일에 auto함수를 선언만하고 include하여 사용하는건 불가능하다 (정의까지 .h에 있고 static으로 선언 시 사용가능)

Trailing Return Type (후행 반환 타입)

후행 반환 타입을 사용하면 auto는 타입 추론을 하지 않고 후행 반환 타입을 사용하기 위한 키워드가 된다

	auto foo(int a, int b) -> int
    {
    	return a + b;
    }
    
    //auto는 int를 사용하기 위한 키워드가 됨

후행 반환 타입은 긴 반환형을 가독성 좋게 만들 수 있으며 타입은 명확하게 확인할 수 있게 한다

함수의 반환형이 매개변수의 타입에 따라 달라질 경우 후행 반환 타입을 사용하면 좋다

    template <typename T1, typename T2>
    auto add(T1 a, T2 b) -> std::common_type_t<T1, T2>
    {
        return a + b;
    }
    
    //or
    
    auto add(int x, double y) -> std::common_type_t<decltype(x), decltype(y)>;
    //이와 같은 코드는 auto를 사용하지 않으면 컴파일 에러가 발생하기 때문에 후행 반환 타입을 사용하여 반환타입을 결정한다

decltype()

decltype(변수 or 표현식)은 해당 변수나 표현식의 타입을 컴파일 타임에 추론해준다

    int x = 10;
    double y = 5.5;

    decltype(x) a; // a는 int로 추론
    decltype(y) b = 3.14; // b는 double로 추론

C++20 이전에는 함수의 매개변수에 auto를 사용할 수 없었지만 C++20이후에는 매개변수에 auto사용이 가능하다, 하지만 타입 추론으로서의 기능이 아닌 함수 템플릿 축약 문법으로 동작한다

이는 다음과 같다

	void printValue(auto value) 
    {
    	std::cout << value << std::endl;
	}
    
    template <typename T>
	void printValue(T value) 
    {
    	std::cout << value << std::endl;
	}
    
    //이 두 함수는 같은 동작을 한다

일반적으로 반환형이 복잡하거나 변경 가능성이 높은경우 auto를 사용한다 (보통 명시적으로 작성하는게 좋음)

profile
GameDeveloper🎮 Dev C++, DataStructure, Algorithm, UE5, Assembly🛠, Git/Perforce🌏

0개의 댓글