int *var = new int (5) // 양쪽 모두에 type specifier가 존재한다.
auto var = new int (5) // 오른쪽에만 type specifier가 존재한다.
auto
는 만능이 아니다. const/volatile/reference에 대한 추론을 컴파일러가 자동으로 하지는 않으므로 반드시 명시해줘야 한다.long long
같은 다중 단어 타입에는 auto
를 사용할 수 없다.auto
의 정확한 타입을 알기 위해서는 IDE의 도움이 필요하다. 즉, 편리한 IDE가 없는 환경에서는 auto가 되려 가독성을 낮추는 결과를 낳을 수 있다.typedef
와 using
은 역할이 동일하다.typedef unsigned char byte == using byte = unsigned char
using
을 사용한 타입 별칭의 진가는 별칭 템플릿에서 나온다.template <typename T>
class userIterator { /*...*/ }
template <typename T>
using vec_t = std::vector<T, userIterator<T>>;
이렇게 사용하면, vec_t라는 별칭을 다양한 자료형에 대해 사용할 수 있으며 더 읽기 쉽고 명확하다는 장점이 있다.{ }
를 이용한 균일한 초기화 덕분에 모두 동일한 방법으로 직접 초기화와 복사 초기화가 가능해졌다.auto var = new int {20};
auto *var {10, 20, 30};
auto classVar {10, "hello", 3.14}; ...
enum
열거형의 여러 문제를 해결하기 위해 범위가 지정된 열거형 enum class
를 도입했다.enum status {Unknown, Created, Connected};
enum result {OK, FAIL, Unknown}; // 오류 발생
auto var = status::Connected; // 오류 발생
enum class status : unsigned int {Unknown, Created, Connected};
enum class result {OK, FAIL, Unknown}; // 오류 발생 안함
auto var = status::Connected; // 오류 발생 안함
enum class
덕분에 이제 더 이상 이름 충돌을 일으키지도 않고 enum
을 정규화된 이름으로 사용할 수도 있다.int var = static_cast<int>(status::Unknown);
auto cont1 = std::vector<int>{10, 0};
for (auto&& itr : cont1)
std::cout << itr << " ";
std::cout << '\n';
auto cont2 = std::map<int, std::string>{{1, "dog"}, {2, "cat"}};
for (auto&& [num, type] : cont2)
std::cout << "Animal number: " << num << " type: " << type;
std::cout << '\n';
범위기반 for 루프의 등장 덕분에 타입을 지정하지 않고 for 루프를 사용할 수 있으며 바인딩 및 분해 (decomposition)도 적재적소로 사용할 수 있게 되었다.
이때, 범위기반 for 루프를 사용자 정의 타입에 대해 정의하기 위해서는 범위기반 for 루프가 어떤 방식으로 동작해야 하는지 알아야 한다.
auto&& __range = range_expression;
auto __begin = begin_expr;
auto __end = end_expr;
for (; __begin != __end; ++__begin) {
range_decalaration = *__begin;
/*...statements...*/
}
상단의 코드를 보아 범위기반 for 루프가 성립되기 위해서는 다음과 같은 것들이 정의되어야 한다는 점을 알 수 있다.
1. begin()
과 end()
함수가 정의되어야 한다.
2. 반복자(Iterator)를 증가시키는 operator++
가 정의되어야 한다.
3. 반복자를 참조하는 operator *
가 정의되어야 한다.
4. 다른 반복자와 비항등성을 비교하는 operator !=
가 정의되어야 한다.
자 이제 상단의 4가지 조건을 모두 성립하는 사용자 정의 타입을 만들어보자.
이때 사용자 정의 타입은 반복자를 가질 수 있도록 배열 기반 타입이어야 한다.
// 사용자 정의 자료형
template <typename T, const size_t Size>
class DummyArray {
private:
T data[Size] = {};
public:
const T& GetAt(const size_t index) const {
if (index < Size) return data[index];
throw std::out_of_range("Index out of range!");
}
void SetAt(const size_t index, const T& value) {
if (index < Size) data[index] = value;
else throw std::out_of_range("Index out of range!");
}
size_t GetSize() { return Size; }
};
// 사용자 정의 자료형을 위한 반복자 클래스 생성
template <typename T, typename C, const size_t Size>
class DummyArrayIteratorType {
private:
size_t index;
C& collection;
public:
explicit DummyArrayIteratorType (C& collection, const size_t index)
: collection(collection), index(index) {}
bool operator != (const DummyArrayIteratorType& other) const {
return index != other.index;
}
const T& operator * () const { return collection.GetAt(index); }
const DummyArrayIteratorType& operator ++ () {
++index;
return *this;
}
};
사용자 정의 자료형에 대한 반복자 클래스를 생성한다. 이때 4가지 조건 중 연산자 오버로딩에 해당하는 3가지 조건을 모두 충족하도록 정의해야 한다.
// 사용자 정의 반복자 클래스를 위한 별칭 템플릿 선언
template <typename T, const size_t Size>
using DummyArrayIterator = DummyArrayIteratorType<T, DummyArray<T, Size>, Size>;
template <typename T, const size_t Size>
using DummyArrayConstIterator = DummyArrayIteratorType<T, DummyArray<T, Size> const, Size>;
해도 그만 안해도 그만이지만 별칭 템플릿을 생성하는 것이 코드를 깔끔하게 만들어줄 것이다. 일반 반복자와 const형 반복자를 만든다.
// begin()과 end() 메소드 생성
// 물론 DummyArrayIteratorType의 멤버로 넣는 방법도 있지만 이 방법이 더 좋다고 한다.
template <typename T, const size_t Size>
inline DummyArrayIterator<T, Size> begin(DummyArray<T, Size>& arr) {
return DummyArrayIterator<T, Size>(arr, 0);
}
template <typename T, const size_t Size>
inline DummyArrayIterator<T, Size> end(DummyArray<T, Size>& arr) {
return DummyArrayIterator<T, Size>(arr, arr.GetSize());
}
template <typename T, const size_t Size>
inline DummyArrayConstIterator<T, Size> begin(const DummyArray<T, Size>& arr) {
return DummyArrayConstIterator<T, Size>(arr, 0);
}
template <typename T, const size_t Size>
inline DummyArrayConstIterator<T, Size> end(const DummyArray<T, Size>& arr) {
return DummyArrayConstIterator<T, Size>(arr, arr.GetSize());
}
일반 반복자와 const형 반복자에 대한 std::begin() 메소드와 std::end() 메소드를 생성한다. 긴 선언문에 비해 내부는 복잡하지 않다.
template <typename T, const size_t Size>
void printDummyArray(DummyArray<T, Size>& arr) {
for (auto&& itr : arr)
cout << itr << " ";
cout << '\n';
}
int main() {
DummyArray<int, 5> arr;
arr.SetAt(0, 10);
arr.SetAt(1, 20);
arr.SetAt(2, 30);
arr.SetAt(3, 40);
arr.SetAt(4, 50);
printDummyArray(arr);
}
이제 사용자가 정의한 자료형에 대해서도 범위 기반 for 루프를 사용할 수 있다.