C++11 에서의 타입추론은 컴파일 시간에 변수의 자료형을 자동으로 추론해준다. 사실 코딩 표준에선 auto보다 실제 자료형을 명시하는것을 선호하지만, 잘만 쓰면 매우 유용한 기능인건 틀림이 없다 ㅇㅅㅇ
프로그래머라면 응당 어떻게 동작하는지 알아야 적재적소에 잘 써먹을 수 있다는 사실..... 타입추론에 관해서 주의할 점도 많으니까 오늘 알아봅시다 ㅋㅅㅋ
타입 추론이 무엇인가?
타입 추론은 컴파일러가 코드 내에서 변수나 표현식의 타입을 "추론"해서 직접 할당해주는 것을 의미하는데요?
이 덕분에 개발자가 직접 타입을 명시하지 않아도 되고, 코드가 간결해질 수 있읍니다.
그렇다면? 타입추론을 자기가 직접 할리는 없고, 해주는 친구들이 있지 않겠습니까?
그 친구들이 바로 auto키워드와 template문법입니다. 오늘은 그중에서 auto에 대하여 알아봅시다 ㅇㅅㅇ
auto가 뭔데 씹덕아
auto는 변수의 타입 (int, char 등)을 지정해줄때의 자료형 대신 auto를 붙여주면 컴파일러가 컴파일시간에 알아서 타입을 추론해주는 친구인데요?
어떤식으로 사용하는지 한번 볼까용
// 사용 예시 ()
double x1 = 5.0; // double 자료형
float x2 = 5.0; // float 자료형
auto x3 = 5.0; // auto의 추론 값 : double
auto x4 = 5.f; // auto의 추론 값 : float
이런식으로 사용할 수 있는 친구입니다 ㅇㅅㅇ 쉽지 아니한가
근데 여기서 촉이 좋으신 분들은 이미 느꼈을텐데요?
5.0을 float에 넣을 수는 있지만 auto에 5.0을 넣으면 double이 추론됩니다. 물론 float를 사용할 때는 숫자 뒤에 "f"를 명시적으로 붙여야 되는건 맞습니다.. 그만큼 auto를 쓸 때는 사용자가 컴파일러에게 타입 추론에 대한 단서를 명확히 줘야 된다는 점입니다. 아래에서 자세히 다뤄볼게여 ㅇㅅㅇ
본격적으로 auto에 대하여 알아보자
여러 상황에 대하여 다뤄볼건데여 통수맞는 상황이 생각보다 많아서 글이 길어질 것 같습니다. ㅠ
같이 추론 값을 알아볼까요?
1. 타입이 다른 연산에 대하여
다음 타입의 추론 값을 예상해보시죠 ㅇㅅㅇ
auto y1 = 1 + 1.0f; // ??
auto y2 = 1.0 + 1.0f; // ??
정답 및 설명
더보기
y1 = float
y2 = double
컴파일러가 타입을 추론할 때 축소 변환을 하지 않는 것을 원칙으로 연산하기 때문인데요?
축소 변환이 무엇인지 대강 말씀드리자면,
(int)1 + (float)1.5f 를 더할 때 컴파일러는 자료형을 암시적으로 통일해서 연산합니다, float를 int로 바꾼다면, 1.5는 1이 되어버립니다. 이 과정에서 원래 수인 1.5를 잃어버리게 되어서 결과적으로 1 + 1 = 2가 되어버리는데요, 반대로 int를 float로 바꾸면 1.0 + 1.5를 연산하여 2.5를 정상적으로 출력할 수 있습니다. float가 int로 변환하면서 값을 잃어버릴 수도 있을 때, 이를 축소 변환한다고 말합니다. 동일하게 float + double 연산도 double이 float에 비해 더 많은 소수점을 나타낼 수 있기 때문에 double로 캐스팅이 됩니다.
똑똑한 컴파일러는 축소 변환으로 인한 연산 미스를 미연에 방지하기 위해 확대 변환을 하여 연산을 합니다. 그래서 auto로 타입 추론시 저런 결과가 나오는 겁니다. ㅇㅅㅇ
2. 상수(const), 참조(&) 추론에 대하여
그럼 이건 어떨까요?
const int z1 = 1; // const int 자료형을 선언
auto z2 = z1; // 그럼 여기서 auto는 const int가 맞겠지?
z2 += 1; // const값을 변경하는데 컴파일 에러가 안난다??
const int z3 = 1; // const int 자료형을 선언
auto& z4 = z3; // 무슨 자료형일까?
z4 += 1; // 이 상황은 에러가 난다
이미 주석으로 결과는 말해드렸는데요?
z2와 z4가 주석의 의도와 다르게 타입 추론이 되었기 때문입니다.
z2와 z4의 타입은 어떻게 추론되었을까요?
정답 및 설명
더보기
z2 = int
z4 = const int&
z2는 const int, 즉 상수 값 1을 대입받아 z2 = 1과 같은 대입 연산이 이루어 졌습니다. 그 결과 int로 추론이 되었습니다. 때문에 z2 += 1 연산이 컴파일 에러없이 잘 동작합니다.
z4는 auto&, 즉 z3의 원본에 대한 값을 참조하므로 타입을 그대로 받아올 수 있었습니다. 그 결과 const int를 참조하기 때문에 const int&로 추론이 되었습니다. 때문에 z4 += 1 연산은 상수연산으로 인식되어 컴파일 에러가 납니다.
3. 포인터(*) 추론에 대하여
C++인데 포인터가 빠지면 섭섭하죠잉? 해당 코드의 추론값도 예상해 보시져 ㅇㅅㅇ
int w1 = 1;
auto w2 = &w1; // w2의 자료형은?
auto* w3 = &w1; // w3의 자료형은?
정답 및 설명
더보기
w2 = int*
w3 = int*
주소를 받았으니 당연히 int*인건 맞습니다. 하지만 w2, w3는 각각 auto, auto*인데 결과가 똑같이 나왔습니다.
이를 통해 컴파일러가 auto추론에서 포인터(*)를 자동으로 추론해주지만, 개발자가 명시적으로 포인터를 정의해줘도 된다는 것을 알았습니다!!
무조건 포인터를 쓸거라는 확신이 있으면 auto에 포인터를 명시해주는 습관을 들이는게 좋겠네용
4. 그외 잡다한 추론
이제 기본적인 추론 상황들은 다 본 것 같습니다...
좀 더 심화적으로 들어가보죠. 다음 상황들도 추론해 보세여 ㅇㅅㅇ
// 다음 변수의 타입을 직접 추론해보자 (안되는 것도 있다)
auto a1 { 1 };
auto a2 = { 1 };
auto b1 = 'abc';
auto b2 = "abc";
auto c1 = { 1,2,3,4 };
auto c2[4] = { 1,2,3,4 };
const int* d1 = 0;
auto d2 = d1;
std::string e1 = "abc";
auto e2 = e1.begin();
const int f1 = 1;
auto&& f2 = f1;
정답 및 간략한 설명
더보기
a1 = int
a2 = std::initializer_list<int>
b1 = int (잘못된 문자형 리터럴 사용. 멀티 캐릭터 리터럴(문자열 및 문자 리터럴(C++) | Microsoft Learn)로 간주해 int로 추론. but 옳지 않은 방식)
b2 = const char*
c1 = std::initializer_list<int>
c2 = 컴파일 에러 (auto는 배열에 대해선 std::initializer_list<>로만 추론한다고 해요. 따라서 []배열선언이 불가능하다고 합니다?)
d2 = const int*
e2 = std::string::iterator
f2 = const int&
번외. 함수 포인터에 대한 추론
void Func1(const char* _str)
{
std::cout << _str << "ㅇㅅㅇ" << '\n';
}
int main()
{
// 함수 포인터를 쓸때 편하다!
void (*funcPtr1)(const char*);
auto funcPtr2 = Func1; // = void (*funcPtr2)(const char*);
}
함수 포인터를 쓸때 정말 ㄹㅇ로 편해집니다?
auto를 쓸 때 주의할 점
auto는 개발자가 제대로 모르고 남발하면 의도된 타입과 다르게 추론될 수 있다는 것을 알았는데요 ㅇㅅㅇ. 때문에 추론 규칙을 완벽히는 아니더라도 잘 숙지해서 쓰는 것도 중요합니다. 변수 위에 마우스 커서를 올리면 추론 값이 명시적으로 나오기 때문에 그걸 확인하는 것도 좋은 습관이 되겠습니다!
또한 auto를 쓰게 되면 코드 간결성은 늘어날 수 있지만, 코드 가독성을 해칠 수 있는데요 ㅇㅅㅇ. 명확한 자료형을 제시함으로써 코드를 읽을 때 어떤 값을 받는지 대충 예상할 수 있지만 auto를 쓰게 되면 확인하기가 힘들기 때문에 코드 가독성면에서 안좋다고 볼 수 있습니다...
마지막으로 auto는 컴파일 시간에 영향을 줍니다. 사실 뭐 엄청나게 컴파일 타임이 늘어나진 않지만, 타입이 복잡해질 경우 조금 영향을 미칠 수 있다고 하네요.
하 알아볼게 왤캐 많아 ㅇㅅㅇ
저는 개인적으로 auto를 비선호합니다(iterator정도는 가끔 씀). 사실 프로그래머라면 auto를 쓰지 않고도 어떤 타입을 써야되는지 알아야 할 필요가 있죠? 그럼에도 코드 간결성은 프로그래머에게 있어서 코스트라고 생각하기 때문에 나쁘게 생각하지는 않습니다. 적재적소에 잘 쓰면 좋은 키워드인건 맞다고 생각합니다 ㅇㅅㅇ
C++14에선 함수의 반환 값 추론도 있다고 하는데요? 그건 그만 알아보자.araboza