Reference initialization
객체에 대한 참조를 바인딩합니다.
목차 |
구문
비 리스트 초기화
T
&
ref
=
target
;
T
|
(1) | ||||||||
T
&&
ref
=
target
;
T
|
(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
|
(1) | ||||||||
T
&&
ref
= {
arg1
,
arg2
,
...
};
T
|
(2) | ||||||||
func-refpar
({
arg1
,
arg2
,
...
});
|
(3) | ||||||||
지정자 목록 초기화 (since C++20)
T
&
ref
= {.
des1
=
arg1
, .
des2
{
arg2
}
...
};
T
|
(1) | ||||||||
T
&&
ref
= {.
des1
=
arg1
, .
des2
{
arg2
}
...
};
T
|
(2) | ||||||||
func-refpar
({.
des1
=
arg1
, .
des2
{
arg2
}
...
});
|
(3) | ||||||||
`, `
`, `
T
에 대한 참조는
T
타입의 객체,
T
타입의 함수, 또는
T
로 암시적으로 변환 가능한 객체로 초기화할 수 있습니다. 일단 초기화되면, 참조는 다른 객체를 참조하도록 재설정(변경)할 수 없습니다.
참조는 다음과 같은 상황에서 초기화됩니다:
설명
| 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 toT1" 타입으로 변환될 수 있다면,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 은 다음 범주 중 하나의 값입니다:
|
(C++11 이전) |
|
(C++11 이후)
(C++17 이전) |
|
(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의 타입을 조정된 타입
이 경우, 참조는 결과 객체 또는 그 적절한 기본 클래스 하위 객체에 바인딩됩니다. |
(since C++17) |
그렇지 않고, 다음 조건들이 모두 충족되는 경우:
-
U는 클래스 타입입니다. -
T는U와 reference-related 관계가 아닙니다. -
target
은 타입
V의 값 v 로 변환될 수 있으며, 이때T는V와 reference-compatible 관계이고, v 는 다음 범주 중 하나에 속합니다:
|
(C++11 이전) |
|
(C++11 이후)
(C++17 이전) |
|
(C++17 이후) |
그러면 참조는 변환 결과에 바인딩되거나, 해당하는 적절한 기본 클래스 하위 객체에 바인딩됩니다:
struct A {}; struct B : A {}; struct X { operator B(); } x; const A& r = x; // 변환 결과의 A 부분 객체에 바인딩됨 B&& rrb = x; // 변환 결과에 직접 바인딩됨
|
변환 결과가 prvalue인 경우,
temporary materialization
이 적용되며, prvalue의 타입을 조정된 타입
이 경우, 참조는 결과 객체 또는 해당 적절한 기본 클래스 하위 객체에 바인딩됩니다. |
(since C++17) |
간접 바인딩
직접 바인딩을 사용할 수 없는 경우, 간접 바인딩이 고려됩니다. 이 경우
T
는
U
와 reference-related일 수 없습니다.
만약
T
나
U
가 클래스 타입인 경우, 사용자 정의 변환은
복사 초기화
규칙을 사용하여
T
타입 객체의 사용자 정의 변환으로 고려됩니다. 해당하는 비참조 복사 초기화가 잘못된 형식이라면 프로그램도 잘못된 형식입니다. 변환 함수 호출의 결과는 비참조
복사 초기화
에 설명된 대로 참조를 직접 초기화하는 데 사용됩니다. 이 직접 초기화에는 사용자 정의 변환은 고려되지 않습니다.
|
그렇지 않으면,
|
(C++17까지) |
|
그렇지 않으면,
target
은 "cv-unqualified
|
(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 는 다음 표현식 중 하나입니다.
이 수명 규칙에는 다음과 같은 예외가 있습니다:
|
(C++26 이전) |
- 함수 호출에서 참조 매개변수에 바인딩된 임시 객체는 해당 함수 호출을 포함하는 전체 표현식이 끝날 때까지 존재합니다: 만약 함수가 전체 표현식보다 오래 생존하는 참조를 반환한다면, 이는 매달린 참조가 됩니다.
|
(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 | 간접 바인딩에 참조 관련 타입이 허용되었음 | 금지됨 |