수색…


소개

루프 문은 조건이 충족 될 때까지 반복적으로 명령문 그룹을 실행합니다. C ++에는 3 가지 유형의 원시 루프가 있습니다 : for, while 및 do ... while.

통사론

  • 동안 ( 조건 ) 성명서 ;
  • while ( 표현 );
  • for ( for-init-statement ; 조건 ; 표현 ) 구문 ;
  • for ( 범위 내 선언 : for-range-initializer ) ;
  • 휴식;
  • 계속;

비고

algorithm 호출은 일반적으로 수작업으로 작성된 루프보다 바람직합니다.

알고리즘이 이미 (또는 매우 비슷한) 것을 원한다면, 알고리즘 호출은 더 명확하고, 종종 더 효율적이고 오류가 발생하기 쉽습니다.

상당히 간단한 작업을하는 루프가 필요한 경우 (그러나 알고리즘을 사용하는 경우 바인더와 어댑터가 혼란 스럽기 때문에) 루프를 작성하십시오.

범위 기반

C ++ 11

for 루프는 숫자 인덱스를 사용하거나 이터레이터에 직접 액세스하지 않고 반복기 기반 범위의 요소를 반복하는 데 사용할 수 있습니다.

vector<float> v = {0.4f, 12.5f, 16.234f};

for(auto val: v)
{
    std::cout << val << " ";
}

std::cout << std::endl;

이것은의 모든 요소를 반복합니다 v 로, val 현재의 요소의 값을 받고. 다음 진술 :

for (for-range-declaration : for-range-initializer ) statement

다음과 같습니다.

{
    auto&& __range = for-range-initializer;
    auto __begin = begin-expr, __end = end-expr;
    for (; __begin != __end; ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}
C ++ 17
{
    auto&& __range = for-range-initializer;
    auto __begin = begin-expr;
    auto __end = end-expr; // end is allowed to be a different type than begin in C++17
    for (; __begin != __end; ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}

이 변경 사항은 C ++ 20의 Ranges TS에 대한 계획된 지원을 위해 도입되었습니다.

이 경우 루프는 다음과 같습니다.

{
    auto&& __range = v;
    auto __begin = v.begin(), __end = v.end();
    for (; __begin != __end; ++__begin) {
        auto val = *__begin;
        std::cout << val << " ";
    }
}

auto val 은 범위에 저장된 값의 복사본이 될 값 유형을 선언합니다 (우리는 iterator에서 복사 초기화됩니다). 범위에 저장된 값이 복사하는 데 비용이 많이 든다면 const auto &val 을 사용할 수 있습니다. auto 을 사용할 필요도 없습니다. 범위의 값 유형에서 암시 적으로 변환 할 수있는 한 적절한 유형 이름을 사용할 수 있습니다.

iterator에 대한 액세스가 필요하다면 range-based for은 당신을 도울 수 없다.

참조하려는 경우 다음과 같이 할 수 있습니다.

vector<float> v = {0.4f, 12.5f, 16.234f};

for(float &val: v)
{
    std::cout << val << " ";
}

const 컨테이너가있는 경우 const 참조를 반복 할 수 있습니다.

const vector<float> v = {0.4f, 12.5f, 16.234f};

for(const float &val: v)
{
    std::cout << val << " ";
}

시퀀스 반복자가 프록시 객체를 반환하고 비 const 방식으로 객체를 조작해야하는 경우 전달 참조를 사용합니다. 참고 : 코드 독자에게 혼동을 줄 수 있습니다.

vector<bool> v(10);

for(auto&& val: v)
{
    val = true;
}

range 기반의 for 제공되는 "range"유형은 다음 중 하나 일 수 있습니다.

  • 언어 배열 :

    float arr[] = {0.4f, 12.5f, 16.234f};
    
    for(auto val: arr)
    {
        std::cout << val << " ";
    }
    

    동적 배열을 할당하는 것은 중요하지 않습니다.

    float *arr = new float[3]{0.4f, 12.5f, 16.234f};
    
    for(auto val: arr) //Compile error.
    {
        std::cout << val << " ";
    }
    
  • 멤버 함수 begin()end() 가있는 모든 유형. 반복자를 해당 유형의 요소로 반환합니다. 표준 라이브러리 컨테이너는 한정되어 있지만 사용자 정의 유형도 사용할 수 있습니다.

    struct Rng
    {
        float arr[3];
    
        // pointers are iterators
        const float* begin() const {return &arr[0];}
        const float* end() const   {return &arr[3];}
        float* begin() {return &arr[0];}
        float* end()   {return &arr[3];}
    };
    
    int main()
    {
        Rng rng = {{0.4f, 12.5f, 16.234f}};
    
        for(auto val: rng)
        {
            std::cout << val << " ";
        }
    }
    
  • 유형에 따라 인수 종속 조회를 통해 찾을 수있는 비 멤버 begin(type)end(type) 함수가있는 모든 type 입니다. 이는 클래스 유형 자체를 수정하지 않고도 범위 유형을 만드는 데 유용합니다.

    namespace Mine
    {
        struct Rng {float arr[3];};
    
        // pointers are iterators
        const float* begin(const Rng &rng) {return &rng.arr[0];}
        const float* end(const Rng &rng) {return &rng.arr[3];}
        float* begin(Rng &rng) {return &rng.arr[0];}
        float* end(Rng &rng) {return &rng.arr[3];}
    }
    
    int main()
    {
        Mine::Rng rng = {{0.4f, 12.5f, 16.234f}};
    
        for(auto val: rng)
        {
            std::cout << val << " ";
        }
    }
    

For 루프

for 루프는 루프 condition 이 참인 동안 loop body 명령문을 실행합니다. 루프 initialization statement 은 정확히 한 번 실행됩니다. 매 사이클마다 iteration execution 부분이 실행됩니다.

for 루프는 다음과 같이 정의됩니다.

for (/*initialization statement*/; /*condition*/; /*iteration execution*/)
{
    // body of the loop
}

자리 표시 자 설명

  • initialization statement :이 문은 for 루프의 시작 부분에서 한 번만 실행됩니다. int i = 0, a = 2, b = 3 과 같이 한 유형의 여러 변수 선언을 입력 할 수 있습니다. 이러한 변수는 루프 범위에서만 유효합니다. 같은 이름의 루프 앞에 정의 된 변수는 루프 실행 중에 숨겨집니다.
  • condition :이 문은 각 루프 본문 실행보다 먼저 평가되고, false 평가되면 루프를 중단합니다.
  • iteration execution :이 문은 for 루프가 본문 에서 중단되지 않는 한 ( break , goto , return 또는 throw되는 예외에 의해) 다음 조건 평가에 앞서 루프 본문 뒤에 실행됩니다. a++, b+=10, c=b+a 와 같이 iteration execution 부분에 여러 명령문을 입력 할 수 있습니다.

while 돌이로 재 작성된 for 돌이의 대략적인 등가물은 다음과 같습니다.

/*initialization*/
while (/*condition*/)
{
    // body of the loop; using 'continue' will skip to increment part below
    /*iteration execution*/
}

for 루프를 사용하는 가장 일반적인 경우는 특정 횟수만큼 명령문을 실행하는 것입니다. 예를 들어, 다음을 고려하십시오.

for(int i = 0; i < 10; i++) {
    std::cout << i << std::endl;
}

유효한 루프 또한 다음과 같습니다.

for(int a = 0, b = 10, c = 20; (a+b+c < 100); c--, b++, a+=c) {
    std::cout << a << " " << b << " " << c << std::endl; 
}

루프 전에 선언 된 변수를 숨기는 예제는 다음과 같습니다.

int i = 99; //i = 99
for(int i = 0; i < 10; i++) { //we declare a new variable i
    //some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 99

그러나 이미 선언 된 변수를 사용하고 숨기지 않으려면 선언 부분을 생략하십시오.

int i = 99; //i = 99
for(i = 0; i < 10; i++) { //we are using already declared variable i
    //some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 10

노트:

  • 초기화 및 증가 명령.은 조건 명령문과 무관 한 조작을 수행 할 수 있습니다. 그러나 가독성을 위해 루프에 직접 관련된 작업 만 수행하는 것이 좋습니다.
  • 초기화 문에서 선언 된 변수는 for 루프의 범위 내에서만 볼 수 있으며 루프가 종료되면 해제됩니다.
  • initialization statement 에서 선언 된 변수는 루프 중에 수정 될 수 있으며 condition 에서 확인 된 변수도 수정할 수 있습니다.

0에서 10까지 카운트하는 루프의 예 :

for (int counter = 0; counter <= 10; ++counter)
{
    std::cout << counter << '\n';
}
// counter is not accessible here (had value 11 at the end)

코드 조각에 대한 설명 :

  • int counter = 0 은 변수 counterint counter = 0 초기화합니다 (이 변수는 for 루프에서만 사용할 수 있습니다).
  • counter <= 10counter 가 10보다 작거나 같은지 여부를 확인하는 부울 조건입니다. true 경우 루프가 실행됩니다. false 이면 루프가 끝납니다.
  • ++counter 는 다음 조건 검사보다 counter 의 값을 1 씩 증가시키는 증가 연산입니다.

모든 문을 비워두면 무한 루프를 만들 수 있습니다.

// infinite loop
for (;;)
    std::cout << "Never ending!\n";

while 루프에 상응하는 내용은 다음과 같습니다.

// infinite loop
while (true)
    std::cout << "Never ending!\n";

그러나 break , goto 또는 return 문을 사용하거나 예외를 throw하여 여전히 무한 루프를 남길 수 있습니다.

<algorithm> 헤더를 사용하지 않고 STL 컬렉션 (예 : vector )의 모든 요소를 ​​반복하는 다음 일반적인 예는 다음과 같습니다.

std::vector<std::string> names = {"Albert Einstein", "Stephen Hawking", "Michael Ellis"};
for(std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
    std::cout << *it << std::endl;
}

While 루프

while 루프는 주어진 조건이 false 평가 될 때까지 반복적으로 명령문을 실행합니다. 이 제어 명령.은 사전에 코드 블록을 몇 번 실행해야하는지 알 수 없을 때 사용됩니다.

예를 들어, 0부터 9까지의 모든 숫자를 인쇄하려면 다음 코드를 사용할 수 있습니다.

int i = 0;
while (i < 10)
{
    std::cout << i << " ";
    ++i; // Increment counter
}
std::cout << std::endl; // End of line; "0 1 2 3 4 5 6 7 8 9" is printed to the console
C ++ 17

C ++ 17부터 처음 두 문장을 결합 할 수 있습니다.

while (int i = 0; i < 10)
//... The rest is the same

무한 루프를 만들려면 다음 구문을 사용할 수 있습니다.

while (true)
{
    // Do something forever (however, you can exit the loop by calling 'break'
}

while 루프의 또 다른 변종, 즉 do...while 구문이 있습니다. 자세한 정보는 do-while 루프 예제 를 참조하십시오.

조건 변수 선언

forwhile 루프의 조건에서 객체를 선언 할 수도 있습니다. 이 객체는 루프의 끝까지 범위에있는 것으로 간주되며 루프의 각 반복을 통해 지속됩니다.

for (int i = 0; i < 5; ++i) {
    do_something(i);
}
// i is no longer in scope.

for (auto& a : some_container) {
    a.do_something();
}
// a is no longer in scope.

while(std::shared_ptr<Object> p = get_object()) {
   p->do_something();
}
// p is no longer in scope.

그러나 do...while 루프를 사용하여 동일한 작업을 수행 할 수 없습니다. 루프 앞에서 변수를 선언하고 루프가 끝난 후에 변수를 범위 밖으로 벗어나게하려면 변수와 루프를 모두 로컬 범위로 묶습니다 (선택 사항).

//This doesn't compile
do {
    s = do_something();
} while (short s > 0);

// Good
short s;
do {
    s = do_something();
} while (s > 0);

이것은 계산서 부 때문이다 do...while 루프 (루프의 본체) 발현 부 (전과 평가 while )에 도달하고, 따라서, 임의 선언은 첫 반복 동안 보이지 않는 것 고리.

Do-while 루프

조건이 아니라 시작에, 각주기의 끝에서 체크를 제외 할 - while 루프, while 루프와 매우 유사합니다. 따라서 루프는 적어도 한 번 이상 실행되도록 보장됩니다.

다음 코드는 0 을 출력합니다. 첫 번째 반복이 끝날 때 조건이 false 로 평가됩니다.

int i =0;
do
{
    std::cout << i;
    ++i; // Increment counter
}
while (i < 0);
std::cout << std::endl; // End of line; 0 is printed to the console

참고 : while(condition); 의 끝에 세미콜론을 잊지 마십시오 while(condition); 이것은 do-while 구조에서 필요합니다.

do-while 루프와 달리 첫 번째 반복의 시작 부분에서 조건이 false 로 평가되기 때문에 다음은 아무 것도 인쇄하지 않습니다.

int i =0;
while (i < 0)
{
    std::cout << i;
    ++i; // Increment counter
}    
std::cout << std::endl; // End of line; nothing is printed to the console

주 : break , goto 또는 return 문을 사용하면 조건이 false가되지 않고 while 루프를 종료 할 수 있습니다.

int i = 0;
do
{
    std::cout << i;
    ++i; // Increment counter
    if (i > 5) 
    {
        break;
    }
}
while (true);
std::cout << std::endl; // End of line; 0 1 2 3 4 5 is printed to the console

사소한 do-while 루프는 종종 자신의 범위가 필요한 매크로를 작성하는 데 사용됩니다 (이 경우 매크로 정의에서 후행 세미콜론은 생략되어 사용자가 제공해야합니다).

#define BAD_MACRO(x) f1(x); f2(x); f3(x);

// Only the call to f1 is protected by the condition here
if (cond) BAD_MACRO(var);

#define GOOD_MACRO(x) do { f1(x); f2(x); f3(x); } while(0)

// All calls are protected here
if (cond) GOOD_MACRO(var);

루프 제어문 : 중단 및 계속

루프 제어 문은 정상적인 순서에서 실행 흐름을 변경하는 데 사용됩니다. 실행이 범위를 벗어나면 해당 범위에서 작성된 모든 자동 개체가 삭제됩니다. breakcontinue 은 루프 제어 문입니다.

break 문은 더 이상 고려하지 않고 루프를 종료합니다.

for (int i = 0; i < 10; i++)
{
    if (i == 4)
        break; // this will immediately exit our loop
    std::cout << i << '\n';
}

위의 코드가 출력됩니다 :

1
2
3

continue 문은 루프를 즉시 종료하지 않고 루프 본문의 나머지 부분을 건너 뛰고 루프의 상단으로 이동합니다 (조건 확인 포함).

for (int i = 0; i < 6; i++)
{
    if (i % 2 == 0) // evaluates to true if i is even
        continue; // this will immediately go back to the start of the loop
    /* the next line will only be reached if the above "continue" statement 
       does not execute  */
    std::cout << i << " is an odd number\n";
}

위의 코드가 출력됩니다 :

1 is an odd number
3 is an odd number
5 is an odd number

이러한 제어 흐름의 변화는 인간이 쉽게 이해할 수없는 경우가 있기 때문에 break 하고 continue 사용하지 마십시오. 보다 직접적인 구현은 일반적으로 읽고 이해하기가 더 쉽습니다. 예를 들어 위의 break 가진 첫 번째 for 루프는 다음과 같이 재 작성 될 수 있습니다.

for (int i = 0; i < 4; i++)
{
    std::cout << i << '\n';
}

continue 가있는 두 번째 예제는 다음과 같이 다시 작성 될 수 있습니다.

for (int i = 0; i < 6; i++)
{
    if (i % 2 != 0) {
        std::cout << i << " is an odd number\n";
    }
}

범위 미만의 범위

범위 기반 루프를 사용하면 범위 기반 루프를위한 프록시 객체를 생성하여 지정된 컨테이너 또는 다른 범위의 하위 부분을 반복 할 수 있습니다.

template<class Iterator, class Sentinel=Iterator>
struct range_t {
  Iterator b;
  Sentinel e;
  Iterator begin() const { return b; }
  Sentinel end() const { return e; }
  bool empty() const { return begin()==end(); }
  range_t without_front( std::size_t count=1 ) const {
    if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
      count = (std::min)(std::size_t(std::distance(b,e)), count);
    }
    return {std::next(b, count), e};
  }
  range_t without_back( std::size_t count=1 ) const {
    if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
      count = (std::min)(std::size_t(std::distance(b,e)), count);
    }
    return {b, std::prev(e, count)};
  }
};

template<class Iterator, class Sentinel>
range_t<Iterator, Sentinel> range( Iterator b, Sentinal e ) {
  return {b,e};
}
template<class Iterable>
auto range( Iterable& r ) {
  using std::begin; using std::end;
  return range(begin(r),end(r));
}

template<class C>
auto except_first( C& c ) {
  auto r = range(c);
  if (r.empty()) return r;
  return r.without_front();
}

이제 우리가 할 수있는 일은 :

std::vector<int> v = {1,2,3,4};

for (auto i : except_first(v))
  std::cout << i << '\n';

인쇄

2
3
4

for 루프의 for(:range_expression) 부분에서 생성 된 중간 객체는 for 루프가 시작될 때까지 만료됩니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow