Copy-initialization
다른 객체로부터 객체를 초기화합니다.
목차 |
구문
T
object
=
other
;
|
(1) | ||||||||
T
object
=
{
other
};
|
(2) | (C++11 이전) | |||||||
f
(
other
)
|
(3) | ||||||||
return
other
;
|
(4) | ||||||||
throw
object
;
|
(5) | ||||||||
T
array
[
N
] = {
other-sequence
};
|
(6) | ||||||||
설명
복사 초기화는 다음과 같은 상황에서 수행됩니다:
T
타입의 명명된 변수(자동, 정적, 또는 스레드-로컬)가 등호 뒤에 표현식으로 구성된 초기화자와 함께 선언될 때.
T
의 명명된 변수가 등호 뒤에 중괄호로 둘러싸인 표현식으로 구성된 초기화자와 함께 선언될 때 (참고: C++11부터는 이것이
목록 초기화
로 분류되며, 축소 변환은 허용되지 않음).
복사 초기화의 효과는 다음과 같습니다:
|
(C++17부터) |
-
첫째
(C++17까지)
그렇지 않으면
(C++17부터)
, 만약
T가 클래스 타입이고 other 의 타입의 cv-unqualified 버전이T이거나T에서 파생된 클래스인 경우,T의 non-explicit 생성자들 을 검사하고 오버로드 해결을 통해 가장 적합한 매치를 선택합니다. 그런 다음 해당 생성자를 호출하여 객체를 초기화합니다.
-
그렇지 않고,
T가 클래스 타입이며, other 의 타입의 cv-unqualified 버전이T가 아니거나T로부터 파생되지 않았거나,T가 비클래스 타입이지만 other 의 타입이 클래스 타입인 경우, other 의 타입에서T(또는T가 클래스 타입이고 변환 함수가 사용 가능한 경우T로부터 파생된 타입)로 변환할 수 있는 사용자 정의 변환 시퀀스 가 검사되고 오버로드 해결을 통해 최적의 시퀀스가 선택됩니다. 변환의 결과는 rvalue 임시 객체 (C++11까지) prvalue 임시 객체 (C++11부터) (C++17까지) prvalue 표현식 (C++17부터) 으로, 변환 생성자 가 사용된 경우T의 cv-unqualified 버전이며, 이 결과가 객체를 직접 초기화 하는 데 사용됩니다. 마지막 단계는 일반적으로 최적화되어 제거 되고 변환 결과가 대상 객체를 위해 할당된 메모리에 직접 생성되지만, 사용되지 않더라도 적절한 생성자(이동 또는 복사)가 접근 가능해야 합니다. (C++17까지)
-
그렇지 않은 경우 (즉,
T와 other 의 타입 모두 클래스 타입이 아닌 경우), 필요한 경우 표준 변환 을 사용하여 other 의 값을T의 cv-unqualified 버전으로 변환합니다.
참고 사항
복사 초기화(copy-initialization)는 직접 초기화(direct-initialization)보다 허용 범위가 제한적입니다: explicit 생성자 는 변환 생성자 가 아니며 복사 초기화 시에는 고려되지 않습니다.
struct Exp { explicit Exp(const char*) {} }; // const char*에서 변환 불가 Exp e1("abc"); // 정상 Exp e2 = "abc"; // 오류, 복사 초기화는 explicit 생성자를 고려하지 않음 struct Imp { Imp(const char*) {} }; // const char*에서 변환 가능 Imp i1("abc"); // 정상 Imp i2 = "abc"; // 정상
또한, 복사 초기화에서의 암시적 변환은 초기화자로부터 직접
T
를 생성해야 하는 반면, 예를 들어 직접 초기화는 초기화자로부터
T
의 생성자 인자로의 암시적 변환을 기대합니다.
struct S { S(std::string) {} }; // std::string에서 암시적으로 변환 가능 S s("abc"); // OK: const char[4]에서 std::string으로 변환 S s = "abc"; // 오류: const char[4]에서 S로 변환 불가 S s = "abc"s; // OK: std::string에서 S로 변환
만약 other 가 rvalue 표현식이라면, move constructor 가 오버로드 해결에 의해 선택되어 복사 초기화 중에 호출됩니다. 이것은 여전히 복사 초기화로 간주됩니다; 이 경우를 위한 특별한 용어(예: 이동 초기화)는 없습니다.
암시적 변환
은 복사 초기화를 기준으로 정의됩니다:
T
타입의 객체가 표현식
E
로 복사 초기화될 수 있다면,
E
는
T
로 암시적으로 변환 가능합니다.
등호 기호,
=
는 명명된 변수의 복사 초기화에서 할당 연산자와 관련이 없습니다. 할당 연산자 오버로드는 복사 초기화에 아무런 영향을 미치지 않습니다.
예제
#include <memory> #include <string> #include <utility> struct A { operator int() { return 12;} }; struct B { B(int) {} }; int main() { std::string s = "test"; // OK: 생성자가 non-explicit임 std::string s2 = std::move(s); // 이 복사 초기화는 이동을 수행함 // std::unique_ptr<int> p = new int(1); // 오류: 생성자가 explicit임 std::unique_ptr<int> p(new int(1)); // OK: 직접 초기화 int n = 3.14; // 부동소수점-정수 변환 const int b = n; // const는 중요하지 않음 int c = b; // ...어떤 방식으로든 A a; B b0 = 12; // B b1 = a; // < 오류: 'A'에서 비스칼라 타입 'B'로의 변환 요청 B b2{a}; // < 동일, A::operator int() 호출 후 B::B(int) 호출 B b3 = {a}; // < auto b4 = B{a}; // < // b0 = a; // < 오류, 대입 연산자 오버로드 필요 [](...){}(c, b0, b3, b4); // 이 변수들이 사용되었다고 가정 }
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 5 | C++98 |
대상 타입의 cv 한정자가 변환 생성자에 의해 초기화된
임시 객체에 적용됨 |
임시 객체는 cv 한정되지 않음 |
| CWG 177 | C++98 |
클래스 객체의 복사 초기화 과정에서 생성된 임시 객체의
값 카테고리가 명시되지 않음 |
rvalue로 명시됨 |