범위 기반 for문 (Range based for loop)
범위 기반 for문은 C++11부터 지원하는 표준 C++ 컨테이너의 기능을 포함하는 컨테이너들에 한하여 사용이 가능한 새로운 형태의 for문이다.
#include <iostream>
#include <array>
#include <vector>
int main() {
std::array<int, 3> arr{ 1, 2, 3 };
std::vector<int> vec{ 4, 5, 6 };
std::cout << "*** std::array ***\n";
for (int& i : arr)
std::cout << i << ", ";
std::cout << "\n*** std::vector ***\n";
for (auto& i : vec)
std::cout << i << ", ";
std::cout << '\n';
return 0;
}
범위 기반 for문은 위의 코드처럼
for([자료형] [요소 이름] : [컨테이너 이름])
의 문법을 가진다.
C++11에서 생긴 std::array와 C++03에서 생긴 std::vector 컨테이너 모두 범위 기반 for문을 사용할 수 있다.
이 때 자료형을 auto로 하여 컴파일 타임에 초기화될 수 있도록 할 수도 있다.
특히 요소의 내용을 바꾸려면 반드시 참조자(&)를 붙여야 한다.
혹은 우측값 참조(&&)를 통해 완벽한 전달을 하여 요소의 내용을 수정할 수도 있다.
실제로 우측값 참조 방식이 권장되는 방법이다.
https://en.cppreference.com/w/cpp/language/range-for
범위 기반 for문을 사용할 수 있는 컨테이너의 조건
그렇다면 범위 기반 for문을 사용할 수 있는 컨테이너의 조건은 무엇일까?
위에서 설명한 C++11 이후의 표준 C++ 컨테이너의 기능을 포함하는 컨테이너란 무엇일까?
마이크로소프트 공식 문서에 따르면 다음과 같다.
Use the range-based for statement to construct loops that must execute through a range, which is defined as anything that you can iterate through—for example, std::vector, or any other C++ Standard Library sequence whose range is defined by a begin() and end().
즉, begin()과 end() 함수가 있고 반복자를 기반으로 순회할 수 있어야 하거나,
시작점과 끝점에 해당하는 주소에 값이 있어야 하고 컨테이너의 길이(sizeof)를 잴 수 있어야 한다.
아래 코드를 보자.
#include <iostream>
void loop(int arr[]) {
for (auto i : arr) // compile error: this range-based 'for' statement requires a suitable "begin" function and none was found
std::cout << i << ", ";
}
int main() {
int arr[3] = { 1,2,3 };
for (auto i : arr) // OK
std::cout << i << ", ";
loop(arr);
return 0;
}
배열 arr는 시작점과 끝점에 해당하는 주소에 값이 있으며 sizeof 연산자로 배열의 길이 역시 알 수 있다.
따라서 일반적으로는 범위 기반 for문을 사용할 수 있다.
하지만 loop함수에서 arr을 포인터로 받은 경우
for문 내에서 이를 배열로 인식하지 못하기 때문에
위와 같은 에러가 나타난다.
때문에 배열을 포인터가 아닌 참조로 받으면 범위 기반 for문을 사용할 수 있다.
배열의 참조를 선언할 때는 반드시 배열의 크기를 지정해주어야 한다.
#include <iostream>
void loop(int (&arr)[3]) {
for (auto i : arr) // OK
std::cout << i << ", ";
}
int main() {
int arr[3] = { 1,2,3 };
for (auto i : arr) // OK
std::cout << i << ", ";
loop(arr);
return 0;
}
함수 loop의 인자를
int arr[] 에서
int (&arr)[3] 로 바꿔주면 된다.
커스텀 컨테이너
언리얼 엔진에는 TArray<T>라고 하는 자료형이 있다.
이 컨테이너는 표준 C++ 컨테이너처럼 begin()과 end() 함수가 존재하며 반복자 역시 있다.
또한 Add, Insert, Append, Remove 와 같은 컨테이너의 수정을 할 수 있는 함수들이 존재한다.
(C#의 List와 매우 흡사하다.)
TArray가 표준 C++ 컨테이너의 기능을 어떻게 구현했는지는 언리얼 엔진에서 커스터마이징한 Array.h에 나타나있다.
// Array.h
// Copyright Epic Games, Inc. All Rights Reserved.
/**
* DO NOT USE DIRECTLY
* STL-like iterators to enable range-based for loop support.
*/
#if TARRAY_RANGED_FOR_CHECKS
FORCEINLINE RangedForIteratorType begin () { return RangedForIteratorType (ArrayNum, GetData()); }
FORCEINLINE RangedForConstIteratorType begin () const { return RangedForConstIteratorType (ArrayNum, GetData()); }
FORCEINLINE RangedForIteratorType end () { return RangedForIteratorType (ArrayNum, GetData() + Num()); }
FORCEINLINE RangedForConstIteratorType end () const { return RangedForConstIteratorType (ArrayNum, GetData() + Num()); }
FORCEINLINE RangedForReverseIteratorType rbegin() { return RangedForReverseIteratorType (ArrayNum, GetData() + Num()); }
FORCEINLINE RangedForConstReverseIteratorType rbegin() const { return RangedForConstReverseIteratorType(ArrayNum, GetData() + Num()); }
FORCEINLINE RangedForReverseIteratorType rend () { return RangedForReverseIteratorType (ArrayNum, GetData()); }
FORCEINLINE RangedForConstReverseIteratorType rend () const { return RangedForConstReverseIteratorType(ArrayNum, GetData()); }
#else
FORCEINLINE RangedForIteratorType begin () { return GetData(); }
FORCEINLINE RangedForConstIteratorType begin () const { return GetData(); }
FORCEINLINE RangedForIteratorType end () { return GetData() + Num(); }
FORCEINLINE RangedForConstIteratorType end () const { return GetData() + Num(); }
FORCEINLINE RangedForReverseIteratorType rbegin() { return RangedForReverseIteratorType (GetData() + Num()); }
FORCEINLINE RangedForConstReverseIteratorType rbegin() const { return RangedForConstReverseIteratorType(GetData() + Num()); }
FORCEINLINE RangedForReverseIteratorType rend () { return RangedForReverseIteratorType (GetData()); }
FORCEINLINE RangedForConstReverseIteratorType rend () const { return RangedForConstReverseIteratorType(GetData()); }
#endif
참조
https://stackoverflow.com/questions/7939399/how-does-the-range-based-for-work-for-plain-arrays
https://cplusplus.com/forum/beginner/192609/
'Programming Languages > C++ Study' 카테고리의 다른 글
C++ MultiThread) std::atomic과 SpinLock (4) | 2024.03.06 |
---|---|
C++) RTTI와 Dynamic Cast (1) | 2024.01.25 |
C++) *(Asterisk, 별표) 연산자 활용하기 (0) | 2024.01.24 |
C++) _CrtIsValidHeapPointer(block) (0) | 2024.01.22 |