Namespaces
Variants

Reference initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
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

객체에 대한 참조를 바인딩합니다.

목차

구문

비 리스트 초기화
T  & ref = target ;

T  & ref ( target );

(1)
T  && ref = target ;

T  && ref ( target );

(2) (C++11부터)
func-refpar ( target ) (3)
return target ; (4) ( func-refret 정의 내부에서)
Class :: Class ( ... ) : ref-member ( target ) { ... } (5) ( Class 정의 내부에서)
일반적인 목록 초기화 (C++11부터)
T  & ref = { arg1 , arg2 , ... };

T  & ref { arg1 , arg2 , ... };

(1)
T  && ref = { arg1 , arg2 , ... };

T  && ref { arg1 , arg2 , ... };

(2)
func-refpar ({ arg1 , arg2 , ... }); (3)
**참고**: 이 C++ 코드 구문은 번역이 필요하지 않은 코드 태그 내에 포함되어 있으므로, HTML 구조와 코드 요소를 그대로 유지하면서 표시만 했습니다. 요청하신 대로 C++ 관련 용어와 코드는 번역하지 않았습니다.
지정자 목록 초기화 (since C++20)
T  & ref = {. des1 = arg1 , . des2 { arg2 } ... };

T  & ref {. des1 = arg1 , . des2 { arg2 } ... };

(1)
T  && ref = {. des1 = arg1 , . des2 { arg2 } ... };

T  && ref {. des1 = arg1 , . des2 { arg2 } ... };

(2)
func-refpar ({. des1 = arg1 , . des2 { arg2 } ... }); (3)
**참고사항:** - HTML 태그와 속성은 번역하지 않고 원본 형식을 유지했습니다 - ` `, `
`, `` 태그 내부의 텍스트는 번역하지 않았습니다
- C++ 관련 용어(des1, des2, arg1, arg2, func-refpar 등)는 번역하지 않았습니다
- 구조화 바인딩 선언 구문은 C++ 전문 용어이므로 그대로 유지했습니다

T 에 대한 참조는 T 타입의 객체, T 타입의 함수, 또는 T 로 암시적으로 변환 가능한 객체로 초기화할 수 있습니다. 일단 초기화되면, 참조는 다른 객체를 참조하도록 재설정(변경)할 수 없습니다.

참조는 다음과 같은 상황에서 초기화됩니다:

1) 명명된 lvalue reference 변수가 초기화자와 함께 선언될 때.
2) 이름이 있는 rvalue reference 변수가 초기화자와 함께 선언될 때.
3) 함수 호출 표현식에서 함수 매개변수가 참조 타입을 가질 때.
4) 반환문에서 함수가 참조 타입을 반환할 때, return 반환된 참조가 임시 표현식 의 결과에 바인딩되는 경우 프로그램의 형식이 올바르지 않습니다. (C++26부터)
5) 비정적 데이터 멤버 가 참조 타입일 때 멤버 초기화자 를 사용하여 초기화되는 경우.

설명

T - 참조되는 타입
ref - 초기화될 참조 변수
target - 사용되는 초기화 표현식
func-refpar - 참조 타입 매개변수를 가진 함수 ( T  & 또는 T  && (C++11부터) )
func-refret - 반환 타입이 참조 타입인 함수 ( T  & 또는 T  && (C++11부터) )
Class - 클래스 이름
ref-member - Class 의 참조 타입 비정적 데이터 멤버 ( T  & 또는 T  && (C++11부터) )
des1 , des2 , ... - 지정자들
arg1 , arg2 , ... - 초기화자 목록의 초기화자들

정의

두 가지 타입 T1 T2 에 대해:

  • cv-비적용 버전의 T1 T2 가 각각 U1 U2 일 때, U1 유사(similar) 하거나, U1 U2 기반 클래스(base class) 인 경우, T1 T2 에 대해 참조 관련(reference-related) 이다.
  • "pointer to T2 " 타입의 prvalue가 표준 변환 순서(standard conversion sequence)를 통해 "pointer to T1 " 타입으로 변환될 수 있다면, T1 T2 참조 호환(reference-compatible) 이다.

초기화 규칙

참조 초기화가 일반 또는 지정된 (C++20부터) 목록 초기화를 사용하는 경우, 목록 초기화 규칙이 적용됩니다.

(C++11부터)

비목록 참조 초기화에서, target 의 타입이 U 로 주어질 때, 참조는 target 직접 바인딩(directly binds) 하거나 target 에서 변환된 T 타입의 값에 바인딩합니다. 직접 바인딩이 먼저 고려되며, 그 다음 간접 바인딩이 고려됩니다. 어느 바인딩도 사용할 수 없는 경우 프로그램은 잘못된 형식(ill-formed)입니다.

두 타입 간의 참조 호환성 관계가 참조 바인딩의 유효성을 설정하는 데 사용되고 표준 변환 시퀀스가 올바르지 않은 형태일 경우, 그러한 바인딩을 필요로 하는 프로그램은 올바르지 않은 형태입니다.

직접 바인딩

다음의 모든 조건이 충족되는 경우:

  • 초기화될 참조는 lvalue 참조입니다.
  • target 은 비트 필드가 아닌 lvalue입니다.
  • T U 와 참조 호환됩니다.

그러면 참조는 target 에 바인딩되거나, 해당하는 적절한 기본 클래스 하위 객체에 바인딩됩니다:

double d = 2.0;
double& rd = d;        // rd는 d를 참조합니다
const double& rcd = d; // rcd는 d를 참조합니다
struct A {};
struct B : A {} b;
A& ra = b;             // ra는 b 내의 A 서브객체를 참조합니다
const A& rca = b;      // rca는 b 내의 A 서브객체를 참조합니다

그렇지 않고, 다음 조건들이 모두 충족되는 경우:

  • 초기화될 참조자는 lvalue 참조입니다.
  • U 는 클래스 타입입니다.
  • T U 와 reference-related 관계가 아닙니다.
  • target T V 와 reference-compatible 관계를 가지도록 V 타입의 lvalue로 변환될 수 있습니다.

그러면 참조는 변환의 lvalue 결과에 바인딩되거나, 그 적절한 기본 클래스 하위 객체에 바인딩됩니다:

struct A {};
struct B : A { operator int&(); };
int& ir = B(); // ir는 B::operator int&의 결과를 참조합니다

그렇지 않으면, 초기화할 참조가 lvalue 참조이고 T 가 const 한정되지 않았거나 volatile 한정된 경우, 프로그램은 잘못된 형식입니다:

double& rd2 = 2.0; // 오류: lvalue가 아니며 참조가 const가 아님
int i = 2;
double& rd3 = i;   // 오류: 타입 불일치 및 참조가 const가 아님

그렇지 않고, 다음 조건들이 모두 충족되는 경우:

  • target 은 다음 범주 중 하나의 값입니다:
  • rvalue
(C++11 이전)
  • non-bit-field xvalue
  • class prvalue
  • array prvalue
  • function lvalue
(C++11 이후)
(C++17 이전)
  • non-bit-field rvalue
  • function lvalue
(C++17 이후)
  • T U 와 참조 호환 가능합니다.

그러면 참조는 target 에 바인딩되거나, 해당하는 적절한 기본 클래스 하위 객체에 바인딩됩니다:

struct A {};
struct B : A {};
extern B f();
const A& rca2 = f(); // B rvalue의 A 하위 객체에 바인딩됨
A&& rra = f();       // 위와 동일
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // i2에 직접 바인딩됨

만약 target 이 prvalue인 경우, temporary materialization 이 적용되며, prvalue의 타입을 조정된 타입 P 로 간주합니다.

  • P target 의 타입(즉 U )으로부터 adjusted 되며, T 의 cv-qualification을 추가합니다.

이 경우, 참조는 결과 객체 또는 그 적절한 기본 클래스 하위 객체에 바인딩됩니다.

(since C++17)

그렇지 않고, 다음 조건들이 모두 충족되는 경우:

  • U 는 클래스 타입입니다.
  • T U 와 reference-related 관계가 아닙니다.
  • target 은 타입 V 의 값 v 로 변환될 수 있으며, 이때 T V 와 reference-compatible 관계이고, v 는 다음 범주 중 하나에 속합니다:
  • rvalue
(C++11 이전)
  • xvalue
  • class prvalue
  • function lvalue
(C++11 이후)
(C++17 이전)
  • rvalue
  • function lvalue
(C++17 이후)

그러면 참조는 변환 결과에 바인딩되거나, 해당하는 적절한 기본 클래스 하위 객체에 바인딩됩니다:

struct A {};
struct B : A {};
struct X { operator B(); } x;
const A& r = x; // 변환 결과의 A 부분 객체에 바인딩됨
B&& rrb = x;    // 변환 결과에 직접 바인딩됨

변환 결과가 prvalue인 경우, temporary materialization 이 적용되며, prvalue의 타입을 조정된 타입 P 로 간주합니다.

  • P 는 변환 결과의 타입으로부터 조정 되며, T 의 cv-qualification을 추가합니다.

이 경우, 참조는 결과 객체 또는 해당 적절한 기본 클래스 하위 객체에 바인딩됩니다.

(since C++17)

간접 바인딩

직접 바인딩을 사용할 수 없는 경우, 간접 바인딩이 고려됩니다. 이 경우 T U 와 reference-related일 수 없습니다.

만약 T U 가 클래스 타입인 경우, 사용자 정의 변환은 복사 초기화 규칙을 사용하여 T 타입 객체의 사용자 정의 변환으로 고려됩니다. 해당하는 비참조 복사 초기화가 잘못된 형식이라면 프로그램도 잘못된 형식입니다. 변환 함수 호출의 결과는 비참조 복사 초기화 에 설명된 대로 참조를 직접 초기화하는 데 사용됩니다. 이 직접 초기화에는 사용자 정의 변환은 고려되지 않습니다.

그렇지 않으면, T 타입의 임시 객체가 생성되고 target 으로부터 복사 초기화됩니다. 참조는 그런 다음 임시 객체에 바인딩됩니다.

(C++17까지)

그렇지 않으면, target 은 "cv-unqualified T " 타입의 prvalue로 암시적으로 변환됩니다. 임시 객체 구체화 변환이 적용되며, prvalue의 타입을 T 로 간주하고 참조는 결과 객체에 바인딩됩니다.

(C++17부터)
const std::string& rs = "abc"; // rs는 char 배열로부터 임시 복사 초기화된 객체를 참조합니다
const double& rcd2 = 2;        // rcd2는 값 2.0을 가진 임시 객체를 참조합니다
int i3 = 2;
double&& rrd3 = i3;            // rrd3는 값 2.0을 가진 임시 객체를 참조합니다

임시 객체의 수명

참조가 임시 객체나 그 하위 객체에 바인딩될 때마다, 임시 객체의 수명은 참조의 수명과 일치하도록 연장됩니다( 임시 객체 수명 예외 사항 확인). 여기서 임시 객체나 그 하위 객체는 다음 표현식 중 하나로 표시됩니다:

(C++17까지)
(C++17부터)
  • 괄호로 묶인 표현식 ( e ) , 여기서 e 는 다음 표현식 중 하나입니다,
  • 내장 첨자 표현식 형태인 a [ n ] 또는 n [ a ] , 여기서 a 는 배열이며 다음 표현식 중 하나입니다,
  • 클래스 멤버 접근 표현식 형태인 e. m , 여기서 e 는 다음 표현식 중 하나이고 m 은 객체 타입의 비정적 데이터 멤버를 지정합니다,
  • 멤버 포인터 연산 형태인 e. * mp , 여기서 e 는 다음 표현식 중 하나이고 mp 는 데이터 멤버에 대한 포인터입니다,
  • 사용자 정의 변환 없이 다음 표현식 중 하나를 피연산자가 지정하는 객체, 또는 그 완전한 객체나 하위 객체를 참조하는 glvalue로 변환하는 const_cast , static_cast , dynamic_cast , 또는 reinterpret_cast 변환 ( 명시적 캐스트 표현식 은 이러한 캐스트들의 시퀀스로 해석됩니다),
  • glvalue인 조건부 표현식 형태인 cond ? e1 : e2 , 여기서 e1 또는 e2 가 다음 표현식 중 하나입니다, 또는
  • glvalue인 내장 쉼표 표현식 형태인 x, e , 여기서 e 는 다음 표현식 중 하나입니다.

이 수명 규칙에는 다음과 같은 예외가 있습니다:

  • 함수의 반환 값에 바인딩된 임시 객체는 return 문에서 확장되지 않습니다: 해당 임시 객체는 반환 표현식이 끝나는 시점에서 즉시 소멸됩니다. 이러한 return 문은 항상 댕글링 참조를 반환합니다.
(C++26 이전)
  • 함수 호출에서 참조 매개변수에 바인딩된 임시 객체는 해당 함수 호출을 포함하는 전체 표현식이 끝날 때까지 존재합니다: 만약 함수가 전체 표현식보다 오래 생존하는 참조를 반환한다면, 이는 매달린 참조가 됩니다.
  • new-표현식의 초기화자에서 참조에 바인딩된 임시 객체는 초기화된 객체의 수명만큼 존재하는 것이 아니라, 해당 new-표현식을 포함하는 전체 표현식의 끝까지 존재합니다. 초기화된 객체가 전체 표현식보다 오래 생존하는 경우, 해당 참조 멤버는 댕글링 참조가 됩니다.
(since C++11)
  • 집계체를 직접 초기화 구문 ( 괄호 ) 을 사용하여 초기화할 때, 참조 요소에 바인딩된 임시 객체는 목록 초기화 구문 { 중괄호 } 과 달리 초기화자를 포함하는 전체 표현식의 끝까지 존재합니다.
struct A
{
    int&& r;
};
A a1{7}; // OK, 수명이 연장됨
A a2(7); // 문법적으로 올바르지만, 댕글링 참조
(C++20부터)

일반적으로 임시 객체의 수명은 "전달"을 통해 더 이상 연장될 수 없습니다: 임시 객체가 바인딩된 참조 변수나 데이터 멤버로부터 초기화된 두 번째 참조는 해당 수명에 영향을 미치지 않습니다.

참고 사항

참조는 함수 매개변수 선언, 함수 반환 타입 선언, 클래스 멤버 선언에서만 초기화 없이 나타나며, extern 지정자와 함께 사용될 때도 초기화 없이 나타납니다.

CWG 이슈 1696 이 해결되기 전까지, 생성자의 initializer list 에서 임시 객체가 참조 멤버에 바인딩되는 것이 허용되었으며, 해당 임시 객체는 객체가 존재하는 동안이 아닌 생성자가 종료될 때까지만 유지되었습니다. 이러한 초기화는 CWG 1696 이후로는 올바르지 않은 형태이지만, 많은 컴파일러들이 여전히 이를 지원합니다 (주목할 만한 예외는 clang입니다).

예제

#include <sstream>
#include <utility>
struct S
{
    int mi;
    const std::pair<int, int>& mp; // 참조 멤버
};
void foo(int) {}
struct A {};
struct B : A
{
    int n;
    operator int&() { return n; }
};
B bar() { return B(); }
//int& bad_r;      // 오류: 초기화자가 없음
extern int& ext_r; // OK
int main()
{
//  좌측값
    int n = 1;
    int& r1 = n;                    // 객체 n에 대한 좌측값 참조
    const int& cr(n);               // 참조는 더 많은 cv-한정자를 가질 수 있음
    volatile int& cv{n};            // 모든 초기화 구문을 사용할 수 있음
    int& r2 = r1;                   // 객체 n에 대한 또 다른 좌측값 참조
//  int& bad = cr;                  // 오류: 더 적은 cv-한정자
    int& r3 = const_cast<int&>(cr); // const_cast가 필요함
    void (&rf)(int) = foo; // 함수에 대한 좌측값 참조
    int ar[3];
    int (&ra)[3] = ar;     // 배열에 대한 좌측값 참조
    B b;
    A& base_ref = b;        // 기본 클래스 부분 객체에 대한 참조
    int& converted_ref = b; // 변환 결과에 대한 참조
//  우측값
//  int& bad = 1;        // 오류: 좌측값 참조를 우측값에 바인딩할 수 없음
    const int& cref = 1; // 우측값에 바인딩됨
    int&& rref = 1;      // 우측값에 바인딩됨
    const A& cref2 = bar(); // B 임시 객체의 A 부분 객체에 대한 참조
    A&& rref2 = bar();      // 동일
    int&& xref = static_cast<int&&>(n); // 직접 n에 바인딩
//  int&& copy_ref = n;                 // 오류: 좌측값에 바인딩할 수 없음
    double&& copy_ref = n;              // 값 1.0을 가진 우측값 임시 객체에 바인딩
//  임시 객체 수명에 대한 제한
//  std::ostream& buf_ref = std::ostringstream() << 'a';
                     // ostringstream 임시 객체는 operator<<의 왼쪽 피연산자에 바인딩되었지만
                     // 그 수명은 세미콜론에서 끝나므로 buf_ref는 댕글링 참조가 됨
    S a {1, {2, 3}}; // 임시 pair {2, 3}이 참조 멤버 a.mp에 바인딩되고
                     // 그 수명은 객체 a의 수명과 일치하도록 연장됨
    S* p = new S{1, {2, 3}}; // 임시 pair {2, 3}이 참조 멤버 p->mp에 바인딩되었지만
                             // 그 수명은 세미콜론에서 끝나므로 p->mp는 댕글링 참조가 됨
    delete p;
    // 다음 변수들에 적용된 [[maybe_unused]]를 모방:
    [](...){}
    (
        cv, r2, r3, rf, ra, base_ref, converted_ref,
        a, cref, rref, cref2, rref2, copy_ref, xref
    );
}

결함 보고서

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

DR 적용 대상 게시된 동작 올바른 동작
CWG 391 C++98 const-qualified 타입에 대한 참조를 클래스 타입 rvalue로 초기화할 때
임시 객체가 생성될 수 있으며, 해당 클래스의 생성자가
rvalue를 임시 객체에 복사하는 데 필요했음
임시 객체가 생성되지 않으며
생성자가 필요하지 않음
CWG 450 C++98 const-qualified 배열에 대한 참조를
참조 호환 가능한 배열 rvalue로 초기화할 수 없었음
허용됨
CWG 589 C++98 참조가 배열 또는 클래스 rvalue에 직접 바인딩될 수 없었음 허용됨
CWG 656 C++98 const-qualified 타입에 대한 참조를 참조 호환 가능하지 않은 타입으로
초기화할 때, 변환 함수가 참조 호환 가능한 타입으로 변환하는 경우
변환 함수의 반환 값(또는 기본 클래스 하위 객체)에서 복사된
임시 객체에 바인딩됨
반환 값(또는 기본 클래스
하위 객체)에 직접 바인딩됨
CWG 1287 C++11 클래스 타입인 target 에서 다른 참조 호환 가능한
타입으로의 변환이 암시적으로만 가능했음
명시적 변환을
허용함
CWG 1295 C++11 참조가 비트 필드 xvalue에 바인딩될 수 있었음 금지됨
CWG 1299 C++98 임시 객체의 정의가 불명확했음 명확하게 정의됨
CWG 1571 C++98 간접 바인딩에서 사용자 정의 변환이
target 의 타입을 고려하지 않았음
고려함
CWG 1604 C++98 간접 바인딩에서 사용자 정의 변환이 고려되지 않았음 고려함
CWG 2352 C++98 참조 호환성이 한정 변환을 고려하지 않았음 고려함
CWG 2481 C++17 간접 바인딩에서 임시 객체 구체화의 결과 타입에
cv-qualification이 추가되지 않았음
추가됨
CWG 2657 C++17 직접 바인딩에서 임시 객체 구체화의 결과 타입에
cv-qualification이 추가되지 않았음
추가됨
CWG 2801 C++98 간접 바인딩에 참조 관련 타입이 허용되었음 금지됨

참고 항목