programing tip

일반 배열에 대한 범위 기반은 어떻게 작동합니까?

itbloger 2020. 10. 22. 07:45
반응형

일반 배열에 대한 범위 기반은 어떻게 작동합니까?


C ++ 11에서는 다른 언어의 for역할을 하는 범위 기반을 사용할 수 foreach있습니다. 일반 C 배열에서도 작동합니다.

int numbers[] = { 1, 2, 3, 4, 5 };
for (int& n : numbers) {
    n *= 2;
}

언제 중지해야하는지 어떻게 알 수 있습니까? for사용되는 것과 동일한 범위에서 선언 된 정적 배열에서만 작동합니까 ? 이것을 for동적 배열과 함께 어떻게 사용 하시겠습니까?


유형이 배열 인 모든 표현식에서 작동합니다. 예를 들면 :

int (*arraypointer)[4] = new int[1][4]{{1, 2, 3, 4}};
for(int &n : *arraypointer)
  n *= 2;
delete [] arraypointer;

보다 자세한 설명을 위해 오른쪽으로 전달 된 표현식의 유형이 :배열 유형 인 경우 루프는에서 ptrto ptr + size( ptr배열의 첫 번째 요소를 가리키며 배열 size의 요소 수)를 반복 합니다.

이것은 클래스 객체를 전달하거나 (그런 식으로 호출되는 멤버가없는 경우) 비 멤버 함수를 전달하는 경우 조회 beginend멤버로 작동하는 사용자 정의 유형과 대조됩니다 . 이러한 함수는 시작 및 종료 반복자를 생성합니다 (각각 마지막 요소 바로 뒤와 시퀀스의 시작을 가리킴).

이 질문은 그 차이가 존재하는 이유를 명확히합니다.


이 질문의 가장 중요한 부분은 C ++가 배열의 크기를 어떻게 아는가입니다 (적어도이 질문을 발견했을 때 알고 싶었습니다).

C ++는 배열 정의의 일부이기 때문에 배열의 크기를 알고 있습니다. 이는 변수의 유형입니다. 컴파일러는 유형을 알아야합니다.

C ++ 11 std::extent을 사용하여 배열의 크기를 얻을 수 있기 때문에 :

int size1{ std::extent< char[5] >::value };
std::cout << "Array size: " << size1 << std::endl;

물론 첫 번째 줄에 크기를 명시 적으로 제공해야하고 두 번째 줄에서 가져와야하기 때문에 이것은별로 의미가 없습니다. 그러나 사용할 수도 decltype있고 더 흥미로워집니다.

char v[] { 'A', 'B', 'C', 'D' };
int size2{ std::extent< decltype(v) >::value };
std::cout << "Array size: " << size2 << std::endl;

최신 C ++ Working Draft (n3376)에 따르면 ranged for 문은 다음과 같습니다.

{
    auto && __range = range-init;
    for (auto __begin = begin-expr,
              __end = end-expr;
            __begin != __end;
            ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}

따라서 for반복자를 사용 하는 일반 루프 와 동일한 방식으로 중지하는 방법을 알고 있습니다.

포인터와 크기 (동적 배열)로만 구성된 배열에 위의 구문을 사용하는 방법을 제공하기 위해 다음과 같은 것을 찾고있을 수 있습니다.

template <typename T>
class Range
{
public:
    Range(T* collection, size_t size) :
        mCollection(collection), mSize(size)
    {
    }

    T* begin() { return &mCollection[0]; }
    T* end () { return &mCollection[mSize]; }

private:
    T* mCollection;
    size_t mSize;
};

그런 다음이 클래스 템플릿을 사용하여 범위를 만들 수 있으며이 범위 에서 새로운 ranged for 구문을 사용하여 반복 할 수 있습니다 . 나는 이것을 사용하여 배열에 대한 포인터와 크기를 별도의 값으로 반환하는 라이브러리를 사용하여 가져온 장면의 모든 애니메이션 개체를 실행하고 있습니다.

for ( auto pAnimation : Range<aiAnimation*>(pScene->mAnimations, pScene->mNumAnimations) )
{
    // Do something with each pAnimation instance here
}

이 구문은 제 생각에 사용하는 것 std::for_each또는 일반 for루프 보다 훨씬 명확 합니다.


정적 배열의 경계를 알고 있기 때문에 중지 할 때를 알고 있습니다.

I'm not sure what do you mean by "dynamic arrays", in any case, if not iterating over static arrays, informally, the compiler looks up the names begin and end in the scope of the class of the object you iterate over, or looks up for begin(range) and end(range) using argument-dependent lookup and uses them as iterators.

For more information, in the C++11 standard (or public draft thereof), "6.5.4 The range-based for statement", pg.145


How does the range-based for work for plain arrays?

Is that to read as, "Tell me what a ranged-for does (with arrays)?"

I'll answer assuming that - Take the following example using nested arrays:

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};

for (auto &pl : ia)

Text version:

ia is an array of arrays ("nested array"), containing [3] arrays, with each containing [4] values. The above example loops through ia by it's primary 'range' ([3]), and therefore loops [3] times. Each loop produces one of ia's [3] primary values starting from the first and ending with the last - An array containing [4] values.

  • First loop: pl equals {1,2,3,4} - An array
  • Second loop: pl equals {5,6,7,8} - An array
  • Third loop: pl equals {9,10,11,12} - An array

Before we explain the process, here are some friendly reminders about arrays:

  • Arrays are interpreted as pointers to their first value - Using an array without any iteration returns the address of the first value
  • pl must be a reference because we cannot copy arrays
  • With arrays, when you add a number to the array object itself, it advances forward that many times and 'points' to the equivalent entry - If n is the number in question, then ia[n] is the same as *(ia+n) (We're dereferencing the address that's n entries forward), and ia+n is the same as &ia[n] (We're getting the address of the that entry in the array).

Here's what's going on:

  • On each loop, pl is set as a reference to ia[n], with n equaling the current loop count starting from 0. So, pl is ia[0] on the first round, on the second it's ia[1], and so on. It retrieves the value via iteration.
  • The loop goes on so long as ia+n is less than end(ia).

...And that's about it.

It's really just a simplified way to write this:

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int n = 0; n != 3; ++n)
  auto &pl = ia[n];

If your array isn't nested, then this process becomes a bit simpler in that a reference is not needed, because the iterated value isn't an array but rather a 'normal' value:

 int ib[3] = {1,2,3};

 // short
 for (auto pl : ib)
   cout << pl;

 // long
 for (int n = 0; n != 3; ++n)
   cout << ib[n];

Some additional information

What if we didn't want to use the auto keyword when creating pl? What would that look like?

In the following example, pl refers to an array of four integers. On each loop pl is given the value ia[n]:

int ia[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for (int (&pl)[4] : ia)

And... That's how it works, with additional information to brush away any confusion. It's just a 'shorthand' for loop that automatically counts for you, but lacks a way to retrieve the current loop without doing it manually.

참고URL : https://stackoverflow.com/questions/7939399/how-does-the-range-based-for-work-for-plain-arrays

반응형