Namespaces
Variants

Range-based for loop (since C++11)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
for
range- for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

범위에 대해 for 루프를 실행합니다.

범위 기반 for 루프는 값의 범위(예: 컨테이너의 모든 요소)에서 동작하는 전통적인 for loop 보다 더 가독성이 높은 대안으로 사용됩니다.

목차

구문

attr  (선택 사항) for ( init-statement  (선택 사항) item-declaration : range-initializer ) statement
attr - 임의의 개수의 attributes
init-statement - (since C++20) 다음 중 하나
(since C++23)

모든 init-statement 는 세미콜론으로 끝나야 합니다. 이것이 흔히 비공식적으로 표현식이나 선언 뒤에 세미콜론이 오는 것으로 설명되는 이유입니다.

item-declaration - 각 범위 항목에 대한 선언
range-initializer - expression 또는 brace-enclosed initializer list
statement - 임의의 statement (일반적으로 복합 문장)

설명

위 구문은 다음 코드와 동등한 코드를 생성합니다 (임시 객체의 수명 확장을 제외하고 range-initializer 의 경우 아래 참조 ) (C++23부터) ( /* */ 로 감싸인 변수와 표현식은 설명 목적으로만 사용됨):

{

auto&& /* range */ = range-initializer  ;
for (auto /* begin */ = /* begin-expr */ , /* end */ = /* end-expr */ ;
/* begin */ != /* end */ ; ++ /* begin */ )
{
item-declaration = * /* begin */ ;
statement
}

}

(C++17 이전)

{

auto&& /* range */ = range-initializer  ;
auto /* begin */ = /* begin-expr */ ;
auto /* end */ = /* end-expr */ ;
for ( ; /* begin */ != /* end */ ; ++ /* begin */ )
{
item-declaration = * /* begin */ ;
statement
}

}

(C++17 이후)
(C++20 이전)

{

init-statement
auto&& /* range */ = range-initializer  ;
auto /* begin */ = /* begin-expr */ ;
auto /* end */ = /* end-expr */ ;
for ( ; /* begin */ != /* end */ ; ++ /* begin */ )
{
item-declaration = * /* begin */ ;
statement
}

}

(C++20 이후)

range-initializer 는 반복할 시퀀스나 범위를 초기화하기 위해 평가됩니다. 시퀀스의 각 요소는 차례로 역참조되어 item-declaration 에 지정된 타입과 이름을 가진 변수를 초기화하는 데 사용됩니다.

item-declaration 은 다음 중 하나일 수 있습니다:

설명 전용 표현식 /* begin-expr */ /* end-expr */ 은 다음과 같이 정의됩니다:

  • /* range */ 의 타입이 배열 타입 R 에 대한 참조인 경우:
  • 만약 R 이 경계 N 을 가진 배열인 경우, /* begin-expr */ /* range */ 이고 /* end-expr */ /* range */ + N 입니다.
  • 만약 R 이 알려지지 않은 경계를 가진 배열이거나 불완전한 타입의 배열인 경우, 프로그램은 형식에 맞지 않습니다.
  • /* range */ 의 타입이 클래스 타입 C 에 대한 참조이고, C 의 범위에서 " begin "과 " end " 이름에 대한 검색이 각각 최소한 하나의 선언을 찾는 경우, /* begin-expr */ /* range */ . begin ( ) 이고, /* end-expr */ /* range */ . end ( ) 입니다.
  • 그렇지 않은 경우, /* begin-expr */ begin ( /* range */ ) 이고, /* end-expr */ end ( /* range */ ) 입니다. 여기서 " begin "과 " end "은 인수 의존 탐색 (비-ADL 탐색은 수행되지 않음)을 통해 찾습니다.

루프가 statement 내에서 종료되어야 하는 경우, break statement 를 종료 문으로 사용할 수 있습니다.

현재 반복을 statement 내에서 종료해야 하는 경우, continue statement 를 단축키로 사용할 수 있습니다.

init-statement 에서 도입된 이름이 statement 의 가장 바깥쪽 블록에서 재선언되면 프로그램의 형식이 올바르지 않습니다:

for (int i : {1, 2, 3})
    int i = 1; // 오류: 재선언

임시 범위 초기화자

만약 range-initializer 가 임시 객체를 반환하면, 해당 객체의 수명은 포워딩 참조 /* range */ 에 바인딩됨으로써 루프가 끝날 때까지 확장됩니다.

range-initializer 내의 모든 임시 객체들의 수명은 연장되지 않습니다 해당 임시 객체들이 range-initializer 끝에서 파괴되는 경우가 아닌 한 (C++23부터) .

// foo()가 값으로 반환하는 경우
for (auto& x : foo().items()) { /* ... */ } // C++23까지 미정의 동작

이 문제는 init-statement 를 사용하여 해결할 수 있습니다:

for (T thing = foo(); auto& x : thing.items()) { /* ... */ } // OK
(C++20부터)


C++23에서도 중간 함수 호출의 비참조 매개변수는 수명 연장을 받지 않습니다(일부 ABI에서는 호출자가 아닌 피호출자에서 파괴되기 때문). 그러나 이는 어차피 버그가 있는 함수에만 해당하는 문제입니다:

using T = std::list<int>;
const T& f1(const T& t) { return t; }
const T& f2(T t)        { return t; } // always returns a dangling reference
T g();
void foo()
{
    for (auto e : f1(g())) {} // OK: lifetime of return value of g() extended
    for (auto e : f2(g())) {} // UB: lifetime of f2's value parameter ends early
}
(C++23부터)

참고 사항

만약 range-initializer braced-enclosed initializer list 인 경우, /* range */ std::initializer_list 에 대한 참조로 추론됩니다.

제네릭 코드에서는 전달 참조에 대해 추론을 사용하는 것이 안전하며, 사실상 바람직합니다, for ( auto && var : sequence ) .

멤버 해석은 범위 타입이 " begin "이라는 멤버와 " end "이라는 멤버를 가질 때 사용됩니다. 이는 해당 멤버가 타입, 데이터 멤버, 함수, 열거자 중 무엇인지와 접근 가능성에 관계없이 수행됩니다. 따라서 다음과 같은 클래스 class meow { enum { begin = 1 , end = 2 } ; /* rest of class */ } ; 는 네임스페이스 범위의 " begin "/" end " 함수가 존재하더라도 범위 기반 for 루프와 함께 사용할 수 없습니다.

item-declaration 에서 선언된 변수는 일반적으로 statement 에서 사용되지만, 이를 반드시 사용해야 하는 것은 아닙니다.

C++17부터, /* begin-expr */ /* end-expr */ 의 타입이 동일할 필요가 없으며, 실제로 /* end-expr */ 의 타입은 반복자일 필요가 없습니다: 단지 반복자와 부등식 비교가 가능하기만 하면 됩니다. 이를 통해 범위를 조건자(예: "반복자가 널 문자를 가리킴")로 구분하는 것이 가능해집니다.

(since C++17)

복사 시 쓰기(copy-on-write) 의미론을 가진 (non-const) 객체와 함께 사용될 때, 범위 기반 for 루프는 (암묵적으로) non-const begin() 멤버 함수를 호출하여 딥 카피를 유발할 수 있습니다.

이것이 바람직하지 않은 경우(예를 들어 루프가 실제로 객체를 수정하지 않는 경우), std::as_const 를 사용하여 피할 수 있습니다:

struct cow_string { /* ... */ }; // a copy-on-write string
cow_string str = /* ... */;
// for (auto x : str) { /* ... */ } // may cause deep copy
for (auto x : std::as_const(str)) { /* ... */ }
(since C++17)
기능 테스트 매크로 표준 기능
__cpp_range_based_for 200907L (C++11) 범위 기반 for 루프
201603L (C++17) 범위 기반 for 루프 ( 서로 다른 begin / end 타입 )
202211L (C++23) 범위 초기화식 내 모든 임시 객체에 대한 수명 확장

키워드

for

예제

#include <iostream>
#include <vector>
int main()
{
    std::vector<int> v = {0, 1, 2, 3, 4, 5};
    for (const int& i : v) // const 참조로 접근
        std::cout << i << ' ';
    std::cout << '\n';
    for (auto i : v) // 값으로 접근, i의 타입은 int
        std::cout << i << ' ';
    std::cout << '\n';
    for (auto&& i : v) // 전달 참조로 접근, i의 타입은 int&
        std::cout << i << ' ';
    std::cout << '\n';
    const auto& cv = v;
    for (auto&& i : cv) // 전달 참조로 접근, i의 타입은 const int&
        std::cout << i << ' ';
    std::cout << '\n';
    for (int n : {0, 1, 2, 3, 4, 5}) // 초기화자는 중괄호로 둘러싸인
                                     // 초기화자 리스트일 수 있음
        std::cout << n << ' ';
    std::cout << '\n';
    int a[] = {0, 1, 2, 3, 4, 5};
    for (int n : a) // 초기화자는 배열일 수 있음
        std::cout << n << ' ';
    std::cout << '\n';
    for ([[maybe_unused]] int n : a)  
        std::cout << 1 << ' '; // 루프 변수를 사용하지 않아도 됨
    std::cout << '\n';
    for (auto n = v.size(); auto i : v) // 초기화문 (C++20)
        std::cout << --n + i << ' ';
    std::cout << '\n';
    for (typedef decltype(v)::value_type elem_t; elem_t i : v)
    // typedef 선언을 초기화문으로 사용 (C++20)
        std::cout << i << ' ';
    std::cout << '\n';
    for (using elem_t = decltype(v)::value_type; elem_t i : v)
    // 별칭 선언을 초기화문으로 사용 (C++23)
        std::cout << i << ' ';
    std::cout << '\n';
}

출력:

0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
0 1 2 3 4 5 
1 1 1 1 1 1 
5 5 5 5 5 5 
0 1 2 3 4 5 
0 1 2 3 4 5

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 1442 C++11 비멤버 " begin " 및 " end " 조회에 일반 비한정 조회가
포함되는지 여부가 명시되지 않았음
일반 비한정 조회를 수행하지 않음
CWG 2220 C++11 init-statement 에서 도입된 이름을 재선언할 수 있었음 이 경우 프로그램이 잘못된 형식임
CWG 2825 C++11 range-initializer 가 중괄호로 묶인 초기화 목록인 경우,
비멤버 " begin " 및 " end "를 조회함
이 경우 멤버 " begin "
및 " end "를 조회함
P0962R1 C++11 멤버 " begin " 및 " end " 중 하나가 존재하는 경우
멤버 해석이 사용됨
둘 다 존재하는 경우에만 사용됨

참고 항목

범위의 요소들에 단항 함수 객체 를 적용합니다
(함수 템플릿)