Move constructors
이동 생성자는 동일한 클래스 타입의 인수를 사용하여 호출될 수 있으며, 인수의 내용을 복사하고 인수를 변경할 수 있는 생성자 입니다.
목차 |
구문
class-name
(
parameter-list
);
|
(1) | ||||||||
class-name
(
parameter-list
)
function-body
|
(2) | ||||||||
class-name
(
single-parameter-list
) = default;
|
(3) | ||||||||
class-name
(
parameter-list
) = delete;
|
(4) | ||||||||
class-name
::
class-name
(
parameter-list
)
function-body
|
(5) | ||||||||
class-name
::
class-name
(
single-parameter-list
) = default;
|
(6) | ||||||||
| class-name | - | 이동 생성자가 선언되는 클래스 |
| parameter-list | - |
다음 모든 조건을 만족하는 비어 있지 않은
매개변수 목록
:
|
| single-parameter-list | - | 단일 매개변수만을 갖는 매개변수 목록 으로, 해당 매개변수는 T && , const T && , volatile T && 또는 const volatile T && 타입이며 기본 인수를 가지지 않음 |
| function-body | - | 이동 생성자의 함수 본문 |
설명
struct X { X(X&& other); // 이동 생성자 // X(X other); // 오류: 잘못된 매개변수 타입 }; union Y { Y(Y&& other, int num = 1); // 다중 매개변수를 가진 이동 생성자 // Y(Y&& other, int num); // 오류: `num`에 기본 인자가 없음 };
이동 생성자는 일반적으로 객체가 동일한 타입의 초기화 ( 직접 초기화 또는 복사 초기화 )될 때 호출되며, 이때 rvalue (xvalue 또는 prvalue) (C++17까지) xvalue (C++17부터) 를 사용하는 경우를 포함합니다.
-
초기화:
T a
=
std
::
move
(
b
)
;
또는
T a
(
std
::
move
(
b
)
)
;
, 여기서
b
는
T타입임; -
함수 인자 전달:
f
(
std
::
move
(
a
)
)
;
, 여기서
a
는
T타입이고 f 는 void f ( T t ) 임; -
함수 반환:
return
a
;
,
T f
(
)
와 같은 함수 내부에서 사용되며, 여기서
a
는
T타입으로 이동 생성자를 가짐.
초기화자가 prvalue인 경우, 이동 생성자 호출은 최적화되어 제거되는 경우가 많습니다 (C++17까지) 수행되지 않습니다 (C++17부터) . 자세한 내용은 copy elision 을 참조하십시오.
이동 생성자는 일반적으로 인수의 자원(예: 동적으로 할당된 객체에 대한 포인터, 파일 디스크립터, TCP 소켓, 스레드 핸들 등)을 복사하는 대신 전송하며, 인수를 유효하지만 불확정적인 상태로 남겨둡니다. 이동 생성자는 인수의 수명을 변경하지 않으므로, 이후 시점에 인수에 대해 소멸자가 호출됩니다. 예를 들어, std::string 또는 std::vector 에서 이동하는 경우 인수가 비어 있는 상태로 남을 수 있습니다. std::unique_ptr 과 같은 일부 타입의 경우, 이동된 후의 상태가 완전히 명시되어 있습니다.
암시적으로 선언된 이동 생성자
클래스 타입에 대해 사용자 정의 이동 생성자가 제공되지 않고, 다음 조건 모두가 참인 경우:
- 사용자 선언 copy constructor 이 없음;
- 사용자 선언 copy assignment operator 가 없음;
- 사용자 선언 move assignment operator 가 없음;
- 사용자 선언 destructor 가 없음.
그러면 컴파일러는 다음과 같은 시그니처를 가진 비- explicit inline public 멤버 이동 생성자를 해당 클래스의 T :: T ( T && ) 로 선언합니다.
클래스는 여러 개의 이동 생성자를 가질 수 있습니다. 예를 들어 T :: T ( const T && ) 와 T :: T ( T && ) 모두 가능합니다. 일부 사용자 정의 이동 생성자가 존재하는 경우에도, 사용자는 default 키워드를 사용하여 암시적으로 선언된 이동 생성자의 생성을 강제할 수 있습니다.
암묵적으로 선언된(또는 첫 선언에서 기본 설정된) 이동 생성자는 다음과 같이 설명된 예외 사양을 가집니다. 동적 예외 사양 (C++17까지) noexcept 사양 (C++17 이후) .
암시적으로 정의된 이동 생성자
암시적으로 선언된 이동 생성자가 삭제되지도 않고 trivial하지도 않다면, 컴파일러에 의해 ODR-use 되거나 상수 평가에 필요할 때 정의됩니다(즉, 함수 본문이 생성되고 컴파일됨). union 타입의 경우, 암시적으로 정의된 이동 생성자는 객체 표현을 복사합니다( std::memmove 에 의한 것처럼). 비-union 클래스 타입의 경우, 이동 생성자는 객체의 직접 기반 서브객체들과 멤버 서브객체들을 초기화 순서에 따라 xvalue 인자를 사용한 직접 초기화로 전체 멤버별 이동을 수행합니다. 참조 타입의 각 비정적 데이터 멤버에 대해, 이동 생성자는 해당 참조를 소스 참조가 바인딩된 동일한 객체나 함수에 바인딩합니다.
이것이
constexpr
constructor
(until C++23)
constexpr
function
(since C++23)
의 요구사항을 만족한다면, 생성된 이동 생성자는
constexpr
입니다.
삭제된 이동 생성자
클래스
T
에 대해 암시적으로 선언되거나 명시적으로 기본 설정된 이동 생성자는 다음과 같은 경우 삭제된 것으로 정의됩니다:
T
가 클래스 타입
M
(또는 다차원 배열)의
잠재적으로 생성되는 하위 객체
를 가지고 있고, 이 하위 객체가
-
M이 삭제되었거나 복사 생성자에서 접근할 수 없는 소멸자를 가지고 있거나, -
M의 이동 생성자를 찾기 위해 적용된 오버로드 해결
-
- 사용 가능한 후보를 생성하지 않거나,
- 하위 객체가 variant member 인 경우, non-trivial 함수를 선택하는 경우.
이러한 생성자는 오버로드 해결 에서 무시됩니다(그렇지 않으면 rvalue로부터의 복사 초기화를 방지할 수 있기 때문입니다).
단순 이동 생성자
클래스
T
의 이동 생성자는 다음 조건이 모두 참일 경우 trivial합니다:
- 사용자가 제공하지 않은 경우(즉, 암시적으로 정의되었거나 기본값인 경우);
-
T가 가상 멤버 함수를 가지지 않는 경우; -
T가 가상 기본 클래스를 가지지 않는 경우; -
T의 모든 직접 기본 클래스에 대해 선택된 이동 생성자가 trivial인 경우; -
T의 모든 비정적 클래스 타입(또는 클래스 타입 배열) 멤버에 대해 선택된 이동 생성자가 trivial인 경우.
사소한 이동 생성자는 사소한 복사 생성자와 동일한 동작을 수행하는 생성자로, 즉 객체 표현을 std::memmove 를 사용한 것처럼 복사합니다. C 언어와 호환되는 모든 데이터 타입은 사소하게 이동 가능합니다.
적격 이동 생성자
|
이동 생성자는 삭제되지 않은 경우 적격입니다. |
(C++20까지) |
|
이동 생성자는 다음 모든 조건을 만족할 경우 적격입니다: |
(C++20부터) |
적격 이동 생성자의 triviality는 클래스가 implicit-lifetime type 인지 여부와 클래스가 trivially copyable type 인지 여부를 결정합니다.
참고 사항
강력한 예외 보장 을 가능하게 하기 위해, 사용자 정의 이동 생성자는 예외를 던지지 않아야 합니다. 예를 들어, std::vector 는 요소들을 재배치해야 할 때 std::move_if_noexcept 를 사용하여 이동과 복사 중 선택합니다.
복사 생성자와 이동 생성자가 모두 제공되고 다른 생성자가 사용 가능하지 않은 경우, 인수가 동일한 타입의 rvalue (예: xvalue 인 std::move 의 결과 또는 이름 없는 임시 객체와 같은 prvalue (C++17까지) )이면 오버로드 해결은 이동 생성자를 선택하고, 인수가 lvalue (이름 있는 객체 또는 lvalue 참조를 반환하는 함수/연산자)이면 복사 생성자를 선택합니다. 복사 생성자만 제공되는 경우, 모든 인수 범주가 이를 선택합니다(rvalue가 const 참조에 바인딩될 수 있으므로, const에 대한 참조를 취하는 경우). 이는 이동을 사용할 수 없을 때 복사가 이동을 위한 대체 수단이 되도록 합니다.
예제
#include <iomanip> #include <iostream> #include <string> #include <utility> struct A { std::string s; int k; A() : s("test"), k(-1) {} A(const A& o) : s(o.s), k(o.k) { std::cout << "move failed!\n"; } A(A&& o) noexcept : s(std::move(o.s)), // 클래스 타입 멤버의 명시적 이동 k(std::exchange(o.k, 0)) // 비클래스 타입 멤버의 명시적 이동 {} }; A f(A a) { return a; } struct B : A { std::string s2; int n; // 암시적 이동 생성자 B::(B&&) // A의 이동 생성자 호출 // s2의 이동 생성자 호출 // n의 비트 단위 복사 수행 }; struct C : B { ~C() {} // 소멸자가 암시적 이동 생성자 C::(C&&)를 방지함 }; struct D : B { D() {} ~D() {} // 소멸자가 암시적 이동 생성자 D::(D&&)를 방지함 D(D&&) = default; // 그래도 이동 생성자를 강제로 생성함 }; int main() { std::cout << "Trying to move A\n"; A a1 = f(A()); // 값 반환 시 함수 매개변수에서 대상을 이동 생성 // 함수 매개변수로부터 std::cout << "Before move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; A a2 = std::move(a1); // xvalue에서 이동 생성 std::cout << "After move, a1.s = " << std::quoted(a1.s) << " a1.k = " << a1.k << '\n'; std::cout << "\nTrying to move B\n"; B b1; std::cout << "Before move, b1.s = " << std::quoted(b1.s) << "\n"; B b2 = std::move(b1); // 암시적 이동 생성자 호출 std::cout << "After move, b1.s = " << std::quoted(b1.s) << "\n"; std::cout << "\nTrying to move C\n"; C c1; C c2 = std::move(c1); // 복사 생성자 호출 std::cout << "\nTrying to move D\n"; D d1; D d2 = std::move(d1); }
출력:
Trying to move A Before move, a1.s = "test" a1.k = -1 After move, a1.s = "" a1.k = 0 Trying to move B Before move, b1.s = "test" After move, b1.s = "" Trying to move C move failed! Trying to move D
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 1353 | C++11 |
기본 이동 생성자가 삭제된 것으로 정의되는 조건에서
다차원 배열 타입을 고려하지 않음 |
이러한 타입을 고려함 |
| CWG 1402 | C++11 |
비트리비얼 복사 생성자를 호출할 기본 이동 생성자는
삭제된 것으로 정의됨; 삭제된 기본 이동 생성자는 오버로드 해결에 여전히 참여함 |
해당 복사 생성자 호출 허용;
오버로드 해결에서 무시됨 |
| CWG 1491 | C++11 |
rvalue 참조 타입의 비정적 데이터 멤버를 가진 클래스의
기본 이동 생성자가 삭제된 것으로 정의됨 |
이 경우 삭제되지 않음 |
| CWG 2094 | C++11 |
volatile 서브오브젝트가 기본 이동 생성자를
비트리비얼하게 만듦 ( CWG 이슈 496 ) |
트리비얼리티에 영향 없음 |
| CWG 2595 | C++20 |
더 제약된 이동 생성자가 존재하지만 해당 연관 제약을
만족하지 않는 경우 이동 생성자가 적격하지 않음 |
이 경우 적격할 수 있음 |