이번엔 C++11에서 새롭게 나온 auto와 decltype에 대해 알아보자.
auto는 타입추론 기능이다. 하나의 자료형으로 볼 수 있으며,
컴파일 타임에 해당 자료형이 결정된다.
auto a=5; //a는 int로 취급
auto b=2.; //b는 double로 취급
vector<int> v={1,2,3};
auto it=v.begin(); //it는 vector<int>::iterator 로 취급
우선 바보가 아닌이상 이 auto
키워드는 다 이해했을 거라고 본다.
보통은 STL 컨테이너의 반복자 타입을 사용할때 auto를 사용한다
사실 auto는 원래 C언어에서 기본 기억저장소 라는 뜻이였다.
(전역변수면 Data영역에 지역변수면 Stack에 만들어라...라는 뜻이다.)
하지만 ㅋ 거의 뜻이 없다고 봐도 무방한데, 이것이 C++11에 오면서 새로운 키워드로 거듭났다.
물론 이제 auto int i=0;
이런 구문은 C++11에서 허용하지 않는다.
이제 C++11에서 auto
는 반복자를 위해서 사용하는것이 옳다고 본다.
#include<iostream> //std::cout , std::endl
#include<algorithm> //for_each
#include<cstdlib> //EXIT_SUCCESS
using namespace std;
int main(){
int arr[5] = { 2, 5, 1, 3, 4 };
//전통적인 C 스타일의 for
for (int i = 0; i < sizeof(arr) / sizeof(int); i++){
cout << arr[i] << endl;
}
//반복자를 이용한 for
for (auto it = begin(arr); it != end(arr); it++){
cout << *it << endl;
}
//c++11에 새로나온 range-based-for
for (auto& e : arr){
cout << e << endl;
}
#if defined(_WIN64) || defined(_WIN32)
//VC++에만 있는 for each (그러나 사용하지 않는것을 추천함)
//range-based-for 문으로 대체하여 사용하길 권장함
for each(auto& e in arr){
cout << e << endl;
}
#endif //defined(_WIN64) || defined(_WIN32)
//algorithm에 있는 for_each
for_each(begin(arr), end(arr), [](int& e)->void{cout << e << endl;});
return EXIT_SUCCESS;
}
뭐...보다 시피 여러가지 방법의 for
가 있다.
그 중에서 for each
는 VC++ 에서만 사용가능하지만 사용하지 말라고 대놓고 써놨다.
https://msdn.microsoft.com/ko-kr/library/ms177202.aspx
이제 머리속에서 for each
는 잊으면 된다.
명심할 점은 for each
와 for_each
는 다른거다.
그럼 잠시 for 문 이야기를 해보자.
C언어에서는 for
, while
, do while
의 3가지 반복문이 있다.
do while
은 사용시기가 정해져 있고, for
는 특정 횟수를, 즉 n번 반복할때
while
은 정확히 몇번 돌지 모를때 사용한다.
C++언어에서는 자기 사용하고 싶은거 사용하면 된다.
range-based-for
는 컨테이너의 전체를 반복할때 사용하면되고 나머지는 때에 맞춰 사용하면 된다.
사실 반복문 보다 중요한게 바로 C++의 배열이다.
C++에 대해 이야기를 좀 하자면, C++은 완전한 객체지향 언어가 아니다.
객체지향을 지원하는 언어이다. JAVA나 C#같은 완전한 객체지향언어는 아니지만,
적어도 C의 잔여물은 사용하지 않았으면 한다.
std::array
c++11에서 새로이 추가된 array라는 컨테이너가 있다.
C의 정적 배열을 대신하는 컨테이너로 #include<array>
를 포함하여 사용할 수 있다.
std::array
는 첨자 접근을 아래의 3가지 방법으로 할 수 있다.
#include<iostream> //std::cout , std::endl , std::cerr
#include<cstdlib> //EXIT_SUCCESS
#include<array> //std::array , std::get
#include<stdexcept> //std::out_of_range
using namespace std;
int main(){
array<int, 5> arr = { 2, 5, 1, 3, 4 };
//바로 접근하는 operator[]
cout << arr[2] << endl;
try{
//out_of_range 예외를 던지는 at()
cout << arr.at(2) << endl;
}
catch (out_of_range& e){
cerr << e.what() << endl;
}
//범위 위반을 컴파일타임에 검사하는 std::get
//오직 array와만 호환된다.
cout << get<2>(arr) << endl;
return EXIT_SUCCESS;
}
그리고 array는 relational operators를 제공한다. 우리가 아는 비교연산과 유사하며
비교는 같은 크기의 std::array
만 가능하며, strcmp
와 비슷하게 첨자별로 순서대로 비교한다.
#include<iostream> //std::cout , std::endl
#include<cstdlib> //EXIT_SUCCESS
#include<array> //std::array
using namespace std;
int main(){
array<int, 5> a = { 1, 2, 3, 4, 5 };
array<int, 5> b = { 2, 2, 2, 2, 2 };
array<int, 5> c = { 2, 2, 2, 2, 2 };
if (a < b)
cout << "b is greater than a" << endl;
if (b == c)
cout << "b equals to c" << endl;
return EXIT_SUCCESS;
}
본 std::array
가 제공하는 메소드는 아래와 같다.
begin 컨테이너의 처음 반복자를 반환한다.
end 컨테이너의 마지막 반복자를 반환한다.
rbegin 컨테이너를 거꾸로 순회하는 첫 반복자를 반환한다.
rend 컨테이너를 거꾸로 순회하는 마지막 반복자를 반환한다.
cbegin c는 const의 뜻이다. write가 불가능한 처음 반복자를 반환한다.
cend write가 불가능한 마지막 반복자를 반환한다.
crbegin const + reverse + begin
crend const + reverse + end
size 배열의 크기를 반환한다.
max_size 배열의 최대 크기를 반환한다.
(vector와 다르게 size 함수와 항상 동일하다)
empty 배열이 비어 있으면 `참` 아니면 `거짓`
operator[] 원소 접근
at 원소 접근(out_of_range 예외 던짐)
front 배열의 첫 원소 반환
back 배열의 마지막 원소 반환
data 배열의 주소 반환
fill 배열을 채우는 메소드
swap 두 컨테이너의 값을 바꾸는 메소드
가능하다면 C 스타일의 배열보단 C++11에서 제공하는 std::array
를 사용하자.
함수의 반환형도 auto로 사용할 수 있다. 물론 아무데서나 사용할 수 있긴 하지만. 무조건 int를 반환하는데 auto로 사용할 필요는 없다.
또한 C++11 ( Visual Studio 2013 , g++ 4.x )에서는 C++14를 사용할 수 없으므로 auto가 반환형인 함수에 대해서는 후행 반환 형식 을 기술하여야 한다.
그렇다 C++은 항상 template이 모든 문제를 야기해 왔다. 앞으로도 그럴것이다.
template과 auto는 같이 사용하면, 먼저 컴파일러가 template으로 사용되는 타입에 대한 [클래스, 함수] 등을 생성한다.
그런데 그런 함수의 반환형으로 auto를 사용하면 해당 템플릿을 추론한뒤, 그 템플릿함수의 반환형까지 추론해야 하기 때문에 컴파일러는 두번을 추론해야 한다.
C++11 컴파일러는 이럴 능력이 안되는데, 따라서 한번더 추론하는 키워드를 넣어줘야한다.
이것이 바로 decltype
이다. 말을 이렇게 했지만 C++11에서 함수의 반환형이 auto이면 무조건 후행반환 형식을 지정해야 한다.
반면 C++14에서는 auto만 써도 타입을 추론한다.
그러나 C++11호환을 위해 후행 반환 형식을 쓰는건 나쁘지 않아 보인다.
decltype
은 declension type 의 약어이다.
#include<iostream> //std::cout , std::endl
#include<cstdlib> //EXIT_SUCCESS
#include<array>
using namespace std;
template<typename T>
auto Add(T a, T b)->decltype(a+b){
return a + b;
}
template<typename Container,typename Index>
auto Nth(Container container, Index index)->decltype(container[index]) {
return container[index];
}
int main(){
array<int,5> arr = { 2, 5, 1, 3, 4 };
cout << Nth(arr, 1) << endl;
cout << Add(2.4, 3.2) << endl;
return EXIT_SUCCESS;
}
Add함수의 경우 두 변수를 더하는데, 자료형에 구애받지 않는다. decltype
은 변수이름을 인수로 받는다.
Nth함수도 해당 컨테이너의 원소의 타입은 container[index] 의 타입이므로 이 예시는 가장 적절하다.
또는 제네릭한 프로그래밍을 할때 auto와 함께 자주 쓰인다.
auto
는 R-value의 식으로 자료형이 결정 나지만, decltype
은 변수명으로 타입을 추론하기 때문에 사용예시가 다르다.
decltype
역시 참조와 포인터를 지정가능하다.