• 4.5.1 typeinfo library header
  • 4.5.2 암시적 형 변환(Implicit Type Conversion(coersion))
  • 4.5.3 명시적 형 변환(Explicit Type Conversion(casting))

4.5.1 typeinfo library header

 

형 변환에 대해서 다루기 전에 유용한 라이브러리 하나를 소개하고자 한다.

#include <iostream>
#include <typeinfo>

int main()
{
	using namespace std;

	cout << typeid(0.0).name() << endl;
	cout << typeid(0).name() << endl;

	bool variable = true;
	cout << typeid(variable).name() << endl;

	return 0;
}

typeinfo 라이브러리에 있는 함수 중 하나인 typeid().name()는 변수나 리터럴이 어떤 타입인지 알려주는 함수이다. 형 변환하면서 제대로 형 변환이 되었는지 체크하기 위해 쓰면 유용한 함수이다.


4.5.2 암시적 형 변환(Implicit Type Conversion(coersion))

 

암시적 형 변환은 컴파일러가 알아서 강제로 형 변환을 해주며, 프로그래머가 아무것도 하지 않아도 자동으로 수행하는 것이다. 아래의 예제에서는 프로그램이 실행되지만 다음과 같은 에러 메시지가 뜬다.

#include <iostream>
#include <typeinfo>

int main()
{
	using namespace std;

	int a = 123.0;
    
	cout << typeid(a).name() << endl;

	return 0;
}
warning C4244: 'initializing': conversion from 'double' to 'int', possible loss of data

C4244 에러는 데이터 손실이 있다고 언급을 하지만 프로그램이 실행되도록 둔다. 개발자가 실수로 값을 잘못 넣는 경우에 발생하니 발견하면 해당 변수에 대해 파악을 하고 수정해두는 것이 좋겠다.

 

빌드는 되지만 신경 쓰이는 에러 메시지가 뜬다.

 

암시적 형변환 중 numeric promotion이라 불리는 형 변환이 있다. numeric promotion은 작은 메모리를 가지는 데이터가 큰 메모리에 담기는 경우를 의미한다. 아래의 예시는 float 자료형이 double로 numeric promotion되었다. 우리말로 숫자 승격이라고 쓸 수 있겠다.

#include <iostream>
#include <typeinfo>

int main()
{
	using namespace std;

	float	f = 1.0f;
	double	d = f;

	cout << typeid(f).name() << endl;
	cout << typeid(d).name() << endl;

	return 0;
}

 

또 다른 암시적 형 변환으로 numeric conversion이 존재한다. 숫자 변환이란 의미를 가지는 이 암시적 형 변환은 서로 다른 자료형 간에 변환이나 큰 메모리를 가지는 데이터가 작은 메모리에 담기는 경우를 말한다. 큰 메모리에 담긴 데이터일지라도 작은 메모리가 감당 가능한 범위의 값이라면 컴파일러가 충분히 처리해준다.

#include <iostream>
#include <typeinfo>
#include <iomanip>

int main()
{
	using namespace std;

	float	d = 3;
	short	s = 2;
	
	cout << typeid(d).name() << endl;
	cout << typeid(s).name() << endl;

	int		i = 30000; // short 범위를 넘어가면 문제지만 안넘어가면 괜찮다.
	char		c = i;

	cout << static_cast<int>(c) << endl; // 48

	double	dd = 0.123456789;
	float		ff = dd;

	cout << setprecision(12) << dd << endl;
	cout << setprecision(12) << ff << endl;

	return 0;
}

4.5.3 명시적 형 변환(Explicit Type Conversion(casting))

 

명시적 형 변환은 프로그래머가 변환하겠다는 강력한 의사표현을 하는 것이다. 아래와 같이 C 스타일과 C++ 스타일, 그리고 최근 자주 쓰는 스타일이 있다.

int main()
{
	using namespace std;

	int i = int(4.0);			// c++ style
	i = (int)4.0;			// c style
	i = static_cast<int>(4.0);	// 최신
	
	return 0;
}

'Programming Language > C++' 카테고리의 다른 글

Section 4.7. 열거형 타입 enum  (0) 2021.11.28
Section 4.6. string 문자열 사용  (0) 2021.11.27
Section 4.4. using namespace  (0) 2021.11.13
Section 4.3. 외부 연결  (0) 2021.11.11
Section 4.2. 전역 변수와 정적 변수  (0) 2021.11.10

C++11에서 auto 키워드가 생겼다. 우리가 기존에 명시해주던 자료형에 대하여 저장하는 데이터 형태를 보고 자료형을 추론해내어 알아서 저장한다. 프로그래밍하면서 굉장히 유용하게 쓸 수 있으니 숙지해두면 좋을 개념이다.


일반적인 변수 초기화

 

일반적으로 하는 변수 초기화를 생각해보자. 변수 이름 앞에 자료형을 기입해주고 초기화해주는 r-value를 해당 자료형에 맞게 입력해준다. 이 경우 해당 변수는 양 옆으로 똑같이 자료형에 대한 정보를 주는 상황이 된다. 즉, 같은 정보가 중복되어서 입력된 것이다. 물론 double로 선언하고 3을 넣는 사람도 있지만 3.0을 넣는게 올바른 프로그래밍이겠다.

#include <iostream>

int main()
{
	using namespace std;

	int a = 123;

	return 0;
}

int 자료형과 r-value 123(int) 정보가 중복되어 들어오는 예시이다.


auto 키워드

 

앞에서 언급한 것처럼 정보가 중복되어 들어오는 것을 비효율적이라 생각하여 auto 키워드가 생겼다. 정보 입력은 하나만 있어도 충분하기 때문이다. auto 키워드를 사용하여 선언을 하는 경우 반드시 초기화도 함께 해주어야 한다. 저장되는 데이터를 컴파일러가 추론해서 어느정도의 메모리를 사용하여 어떤 기본 자료형으로 저장할지 선택해야 하는데, 애초에 저장할 데이터를 안 주면 추론 자체가 불가능하니 당연하겠다.

#include <iostream>

int main()
{
	using namespace std;

	int a = 123;

	auto b = 123123123123123123;
	auto d = 123.0;
	auto c = 1 + 2.0;

	return 0;
}

이렇게 auto로 초기화한 변수에 마우스 커서를 올리면 어떤 자료형으로 추론이 되었는지 알 수 있다.

long long으로 추론되었다.

이렇게 한 번 추론이 되면 추론된 자료형으로 취급하여 다루면 되겠다.


함수에서의 auto 키워드

 

auto 키워드는 함수를 다룰 때도 사용할 수 있다.

int add(int x, int y)
{
	return x + y;
}

int main()
{
	using namespace std;

	auto result = add(1, 2);

	return 0;
}

이처럼 함수에서 return 값을 auto로 받아낼 수 있겠다. 위의 코드에서 add 함수를 아래 두가지로 변경해서 넣어보자.

auto add(int x, int y)
{
	return x + y;
}

auto add(int x, int y)
{
	return x + (double)y;
}

정의부터 auto를 써서 사용한다. 애초에 return에 놓인 결과값도 int이기 때문에 추론이 가능한 형태이다. 두 번째 add 함수는 return하기 전에 double로 형 변환을 수행하였다. 이럴 경우 return 값이 double로 변하고 해당 함수의 return 값을 받아야 하는 변수도 double이 되어야 한다. 전부 auto로 연결되어서 문제없이 작동하는 것을 확인할 수 있다.

 

매개 변수 위치에는 auto 키워드를 쓸 수 없다. 매개 변수에 auto 키워드를 쓰는 목적을 생각해보면 함수 오버로딩을 안 하고 한 번에 정의하기 위함일 듯하다. 그러한 목적을 충족시켜주는 template 개념이 이미 존재하기 때문에 template을 활용하여 사용하면 되겠다.

 

auto 키워드가 생기면서 function trailing return type이라는 개념으로 프로그래밍이 가능해졌다. 결과가 되는 자료형을 아래처럼 함수 뒤에 적는 것이다. 굳이 적을 필요가 있냐는 생각이 들 수 있지만 추후 다루게 될 template과 사용할 때 효과를 발휘한다. 또한 함수의 결과가 무엇인지 좌에서 우로 순차적인 형태가 되기 때문에 C++이 익숙하지 않은 프로그래머에게 가독성을 높이는 코딩이라 할 수 있겠다.

auto add(int x, int y) -> double;

int main()
{
	using namespace std;

	auto result = add(1, 2);

	return 0;
}

auto add(int x, int y) -> double
{
	return x + (double)y;
}

Reference

  • 4.4.1 using namespace
  • 4.4.2 사용 시 유의사항

4.4.1 using namespace

 

using namespace는 많은 C++ 개발자들이 사용하는 키워드이다. 해당 키워드를 사용해서 코드 타이핑할 때 네임스페이스를 쓰는 시간을 절약할 수 있다.

using namespace std;

대표적으로 std를 생략해서 코드를 작성할 수 있겠다.


4.4.2 사용 시 유의사항

 

편하게 하는만큼 사용할 때 주의해야 하는 부분들이 있다. 우선 using namespace를 선언한 순간 해당 선언 부분이 속한 블럭에서는 선언 이후 계속 적용된다는 점이다.

#include <iostream>

int main()
{
	using namespace std;

	cout << "Hello " << endl;

	return 0;
}

이처럼 main 함수 안에서는 std를 안쓰고 사용할 수 있지만 해당 함수 내부에서 선언한 것을 취소할 수 있는 기능은 주어지지 않는다. 따라서 적용시킬 부분만 적용되도록 최대한 사용하는 곳과 가까이 선언하자! 전역 위치에 선언하는건 좋지 않으니 위처럼 지역적으로 묶어서 사용하자. 또한 헤더 파일에 using namespace를 선언해버리면 해당 헤더 파일을 include하는 곳에 전부 영향을 미치니 헤더와 cpp로 분리하고 cpp에 선언해주는 습관을 가지자!

 

using namespace 대신 using만 사용하여 일부 함수들에 대해서만 생략을 적용시킬 수 있다.

#include <iostream>

int main()
{
	using std::cout;
	using std::endl;

	cout << "Hello " << endl;

	return 0;
}

 

아래처럼 namespace가 존재하고 using namespace를 사용하면 어떤 namespace의 my_var 변수를 사용하는지 모호해진다. 이때는 영역 지정 연산자(::)를 사용하여 소속을 확실히 해주자!

#include <iostream>

namespace a
{
	int my_var(10);
	int my_a(123);
}

namespace b
{
	int my_var(20);
	int my_b(456);
}

int main()
{
	using std::cout;
	using std::endl;

	using namespace a;
	using namespace b;

	cout << my_var << endl; // a와 b에 둘다 있으니 모호해짐
	cout << a::my_var << endl; // 영역지정 연산자를 통해 해결 가능

	return 0;
}

 

최대한 잘게 쪼개서 작은 영역에 using namespace가 영향력을 행사하도록 작성하는 것도 하나의 방법이다.

// using문과 모호성

#include <iostream>

namespace a
{
	int my_var(10);
	int my_a(123);
}

namespace b
{
	int my_var(20);
	int my_b(456);
}


int main()
{
	using std::cout;
	using std::endl;

	{
		using namespace a;
		cout << my_var << endl;
	}

	{
		using namespace b;
		cout << my_var << endl;
	}

	return 0;
}

+ Recent posts