
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 타입으로 추론된다
단점
함수에서의 타입 추론
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를 사용한다 (보통 명시적으로 작성하는게 좋음)