copy_initialization — 복사 초기화
copy_initialization — 복사 초기화 (Copy-initialization)
**복사 초기화(copy-initialization)**는 다른 객체로부터 객체를 초기화하는 방식이에요. = 기호를 사용해 초기화하거나, 값 전달, 값 반환, 예외 throw/catch 같은 상황에서 일어나요.
T a = b; 형태로 객체를 만들 때 적용돼요. 복사 초기화는 explicit 생성자를 고려하지 않는다는 점이 직접 초기화와 달라요.
출처: cppreference
본문
복사 초기화는 다른 객체로부터 객체를 초기화해요.
문법 (Syntax)
T object = other;
T object = {other};
f(other)
return other;
throw object;
catch (T object)
T array[N] = {other-sequence};
설명 (Explanation)
복사 초기화는 다음 상황에서 수행돼요.
- 비참조 타입
T의 이름 있는 변수(자동, 정적, 스레드 로컬)가 등호 뒤에 표현식이 오는 초기화자로 선언될 때 - 스칼라 타입
T의 이름 있는 변수가 등호 뒤에 중괄호로 감싼 표현식으로 선언될 때 (C++11부터 이건 목록 초기화로 분류되고, 수축 변환은 허용되지 않아요) - 함수에 인자를 값으로 전달할 때
- 값을 반환하는 함수에서 반환할 때
- 예외를 값으로 던지거나 잡을 때
- 애그리거트 초기화의 일부로, 초기화자를 제공받은 각 요소를 초기화할 때
복사 초기화의 효과
T가 클래스 타입이고 초기화자가T와 (cv 한정을 무시하고) 같은 클래스인 prvalue 표현식이면, 그 prvalue에서 만들어진 임시가 아니라 초기화자 표현식 자체가 목적 객체를 초기화하는 데 사용돼요 (복사 생략 참조).- 그 외에
T가 클래스 타입이고other의 cv 한정 없는 타입이T거나T에서 파생된 클래스라면,T의 비-explicit생성자를 검사해 과부하 해석으로 최적 매치를 선택하고 그 생성자로 객체를 초기화해요. - 그 외에
T가 클래스 타입이고other의 타입이T거나 그 파생이 아니거나,T가 비클래스 타입인데other의 타입이 클래스 타입이면,other의 타입에서T로(또는 변환 함수가 있으면T에서 파생된 타입으로) 변환할 수 있는 사용자 정의 변환이 검사되고 과부하 해석으로 최적이 선택돼요. 변환 결과(변환 생성자라면 cv 한정 없는T의 prvalue 임시)로 객체를 직접 초기화해요. (복사 생략으로 결과를 목적 객체 메모리에 직접 생성할 수 있지만, 적절한 생성자가 사용되지 않더라도 접근 가능해야 해요.) - 그 외 (네
T도other의 타입도 클래스 타입이 아니면), 필요하다면 표준 변환으로other의 값을 cv 한정 없는T로 변환해요.
노트 (Notes)
복사 초기화는 직접 초기화보다 덜 허용적이에요. explicit 생성자는 변환 생성자가 아니므로 복사 초기화에서 고려되지 않아요. other가 rvalue 표현식이면 복사 초기화 중에 이동 생성자가 선택될 수 있는데, 이것도 여전히 복사 초기화로 간주돼요.
암시적 변환은 복사 초기화의 관점에서 정의돼요. 이름 있는 변수의 복사 초기화에서의 등호 =는 대입 연산자와 관련이 없어요. 대입 연산자 오버로드는 복사 초기화에 영향을 주지 않아요.
예제 (Example)
#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); // 사용된 척
}
std::string s = "test"처럼 등호를 쓰는 복사 초기화는 비-explicit 생성자만 고려해요. std::unique_ptr은 explicit 생성자라서 = new int(1)로는 초기화할 수 없고, new int(1) 괄호 직접 초기화만 가능해요.