Move assignment operator
이동 할당 연산자는 동일한 클래스 타입의 인수로 호출될 수 있으며, 인수의 내용을 복사하고 인수를 변경할 수 있는 이름이 operator = 인 비템플릿 비정적 멤버 함수 입니다.
목차 |
구문
공식적인 이동 할당 연산자 구문에 대해서는 함수 선언 을 참조하십시오. 아래 구문 목록은 유효한 모든 이동 할당 연산자 구문의 일부만 보여줍니다.
반환-타입
operator=(
매개변수-목록
);
|
(1) | ||||||||
반환-타입
operator=(
매개변수-목록
)
함수-본문
|
(2) | ||||||||
반환-타입
operator=(
기본값-없는-매개변수-목록
) = default;
|
(3) | ||||||||
반환-타입
operator=(
매개변수-목록
) = delete;
|
(4) | ||||||||
반환-타입
클래스-이름
::
operator=(
매개변수-목록
)
함수-본문
|
(5) | ||||||||
반환-타입
클래스-이름
::
operator=(
기본값-없는-매개변수-목록
) = default;
|
(6) | ||||||||
| class-name | - |
이동 대입 연산자가 선언되는 클래스, 클래스 타입은 아래 설명에서
T
로 주어짐
|
| parameter-list | - |
T&&
,
const
T
&&
,
volatile
T
&&
또는
const
volatile
T
&&
타입의 단일 매개변수만을 갖는
매개변수 목록
|
| parameter-list-no-default | - |
T&&
,
const
T
&&
,
volatile
T
&&
또는
const
volatile
T
&&
타입의 단일 매개변수만을 가지며 기본 인수가 없는
매개변수 목록
|
| function-body | - | 이동 대입 연산자의 함수 본문 |
| return-type | - |
모든 타입, but
T&
is favored in order to be consistent with scala types
|
설명
struct X { X& operator=(X&& other); // 이동 대입 연산자 // X operator=(const X other); // 오류: 잘못된 매개변수 타입 }; union Y { // 이동 대입 연산자는 위에 나열된 구문 외에도 // 일반 함수 선언 구문을 따르고 // 위에 나열된 제한 사항을 위반하지 않는 한 다양한 구문을 가질 수 있음 auto operator=(Y&& other) -> Y&; // OK: 후행 반환 타입 Y& operator=(this Y&& self, Y& other); // OK: 명시적 객체 매개변수 // Y& operator=(Y&&, int num = 1); // 오류: 다른 비객체 매개변수를 가짐 };
이동 할당 연산자는 오버로드 해결 에 의해 선택될 때마다 호출됩니다. 예를 들어, 객체가 할당 표현식의 좌변에 나타나고 우변이 동일한 타입이나 암시적으로 변환 가능한 타입의 rvalue인 경우입니다.
이동 할당 연산자는 일반적으로 인자의 자원(예: 동적으로 할당된 객체에 대한 포인터, 파일 디스크립터, TCP 소켓, 스레드 핸들 등)을 복사하는 대신 전송하며, 인자를 유효하지만 불확정 상태로 남깁니다. 이동 할당은 인자의 수명을 변경하지 않으므로, 이후 시점에 인자의 소멸자가 일반적으로 호출됩니다. 예를 들어, std::string 또는 std::vector 에서 이동 할당을 수행하면 인자가 비어 있는 상태로 남을 수 있습니다. 이동 할당은 일반 할당보다 제한이 적게 정의됩니다; 일반 할당이 완료 시 두 개의 데이터 복사본을 남겨야 하는 반면, 이동 할당은 하나의 복사본만 남기도록 요구됩니다.
암시적으로 선언된 이동 할당 연산자
클래스 타입에 대해 사용자 정의 이동 대입 연산자가 제공되지 않고, 다음 조건 모두가 참인 경우:
- 사용자 선언 copy constructors 가 없는 경우;
- 사용자 선언 move constructors 가 없는 경우;
- 사용자 선언 copy assignment operators 가 없는 경우;
- 사용자 선언 destructor 가 없는 경우,
그러면 컴파일러는 다음과 같은 시그니처를 가진 이동 할당 연산자를 해당 클래스의 inline public 멤버로 선언합니다: T & T :: operator = ( T && ) .
클래스는 여러 개의 이동 대입 연산자를 가질 수 있습니다. 예를 들어
T
&
T
::
operator
=
(
const
T
&&
)
와
T
&
T
::
operator
=
(
T
&&
)
모두를 가질 수 있습니다. 사용자 정의 이동 대입 연산자가 존재하는 경우에도, 사용자는
default
키워드를 사용하여 암시적으로 선언된 이동 대입 연산자의 생성을 강제할 수 있습니다.
암시적으로 선언된 이동 대입 연산자는 다음과 같이 설명된 예외 사양을 가집니다. 동적 예외 사양 (C++17까지) noexcept 사양 (C++17부터) .
일부 대입 연산자(이동 또는 복사)가 항상 모든 클래스에 대해 선언되기 때문에, 기본 클래스의 대입 연산자는 항상 숨겨집니다. using 선언을 사용하여 기본 클래스에서 대입 연산자를 가져오고, 그 인수 타입이 파생 클래스의 암시적 대입 연산자의 인수 타입과 동일할 수 있는 경우, using 선언도 암시적 선언에 의해 숨겨집니다.
암시적으로 정의된 이동 할당 연산자
암시적으로 선언된 이동 대입 연산자가 삭제되지도 않고 trivial하지도 않다면, 컴파일러에 의해 정의됩니다(즉, 함수 본문이 생성되고 컴파일됨). 이는 odr-used 되거나 needed for constant evaluation (C++14부터) 일 때 발생합니다.
공용체(union) 타입의 경우, 암시적으로 정의된 이동 할당 연산자는 객체 표현을 복사합니다 ( std::memmove 에 의한 것처럼).
비-공용체 클래스 타입의 경우, 이동 할당 연산자는 객체의 직접 기반 클래스와 직접 비정적 멤버들에 대해 선언 순서대로 완전한 멤버별 이동 할당을 수행합니다. 스칼라 타입에는 내장 할당을, 배열에는 멤버별 이동 할당을, 클래스 타입에는 이동 할당 연산자를 사용합니다(비가상으로 호출됨).
|
클래스
|
(C++14부터)
(C++23 이전) |
|
클래스
|
(C++23부터) |
복사 할당과 마찬가지로, 상속 구조에서 여러 경로를 통해 접근 가능한 가상 기본 클래스 하위 객체가 암시적으로 정의된 이동 할당 연산자에 의해 여러 번 할당되는지 여부는 명시되지 않습니다:
struct V { V& operator=(V&& other) { // 이 함수는 한 번 또는 두 번 호출될 수 있음 // 두 번 호출되는 경우, 'other'는 방금 이동된 V 하위 객체임 return *this; } }; struct A : virtual V {}; // operator=는 V::operator=를 호출함 struct B : virtual V {}; // operator=는 V::operator=를 호출함 struct C : B, A {}; // operator=는 B::operator=를 호출한 다음 A::operator=를 호출함 // 하지만 V::operator=는 한 번만 호출할 수 있음 int main() { C c1, c2; c2 = std::move(c1); }
삭제된 이동 할당 연산자
암시적으로 선언되거나 기본 설정된 클래스
T
의 이동 할당 연산자는 다음 조건 중 하나라도 충족될 경우 삭제된 것으로 정의됩니다:
-
T가 const 한정된 비클래스 타입(또는 다차원 배열)의 비정적 데이터 멤버를 가지고 있는 경우 -
T가 참조 타입의 비정적 데이터 멤버를 가지고 있는 경우 -
T가 클래스 타입M(또는 다차원 배열)의 잠재적으로 생성되는 하위 객체 를 가지고 있어,M의 이동 할당 연산자를 찾기 위한 오버로드 해결이 적용되는 경우
-
- 사용 가능한 후보를 생성하지 않거나,
- 하위 객체가 variant member 인 경우, non-trivial 함수를 선택하는 경우.
삭제된 암시적으로 선언된 이동 할당 연산자는 오버로드 해결 에서 무시됩니다.
단순 이동 할당 연산자
클래스
T
의 이동 할당 연산자는 다음 조건이 모두 참일 경우 trivial합니다:
- 사용자가 제공하지 않은 경우(즉, 암시적으로 정의되었거나 기본값인 경우);
-
T가 가상 멤버 함수를 가지지 않는 경우; -
T가 가상 기본 클래스를 가지지 않는 경우; -
T의 모든 직접 기본 클래스에 대해 선택된 이동 대입 연산자가 trivial한 경우; -
T의 모든 비정적 클래스 타입(또는 클래스 타입 배열) 멤버에 대해 선택된 이동 대입 연산자가 trivial한 경우.
사소한 이동 할당 연산자는 사소한 복사 할당 연산자와 동일한 작업을 수행합니다. 즉, 객체 표현을 std::memmove 를 사용하는 것처럼 복사합니다. C 언어와 호환되는 모든 데이터 타입은 사소하게 이동 할당이 가능합니다.
적격 이동 할당 연산자
|
이동 대입 연산자는 삭제되지 않은 경우 적격합니다. |
(C++20까지) |
|
이동 대입 연산자는 다음 모든 조건이 충족될 경우 적격합니다: |
(C++20부터) |
적격 이동 대입 연산자의 triviality는 클래스가 trivially copyable type 인지 여부를 결정합니다.
참고 사항
복사 할당 연산자와 이동 할당 연산자가 모두 제공되는 경우, 인수가 rvalue (이름 없는 임시 객체와 같은 prvalue 또는 std::move 의 결과와 같은 xvalue )이면 이동 할당을 선택하고, 인수가 lvalue (이름 있는 객체 또는 lvalue 참조를 반환하는 함수/연산자)이면 복사 할당을 선택합니다. 복사 할당만 제공되는 경우, 모든 인수 범주가 이를 선택합니다 (rvalue가 const 참조에 바인딩될 수 있으므로 인수를 값으로 받거나 const에 대한 참조로 받는 경우). 이는 이동을 사용할 수 없을 때 복사 할당이 이동 할당을 위한 대체 수단이 되도록 합니다.
상속 구조에서 여러 경로를 통해 접근 가능한 가상 기본 클래스 서브오브젝트가 암시적으로 정의된 이동 할당 연산자에 의해 여러 번 할당되는지 여부는 지정되지 않습니다( 복사 할당 도 동일하게 적용됩니다).
사용자 정의 이동 할당 연산자의 예상 동작에 대한 추가 정보는 할당 연산자 오버로딩 을 참조하십시오.
예제
#include <iostream> #include <string> #include <utility> struct A { std::string s; A() : s("test") {} A(const A& o) : s(o.s) { std::cout << "move failed!\n"; } A(A&& o) : s(std::move(o.s)) {} A& operator=(const A& other) { s = other.s; std::cout << "copy assigned\n"; return *this; } A& operator=(A&& other) { s = std::move(other.s); std::cout << "move assigned\n"; return *this; } }; A f(A a) { return a; } struct B : A { std::string s2; int n; // 암시적 이동 할당 연산자 B& B::operator=(B&&) // A의 이동 할당 연산자 호출 // s2의 이동 할당 연산자 호출 // n의 비트 단위 복사 수행 }; struct C : B { ~C() {} // 소멸자가 암시적 이동 할당을 방지함 }; struct D : B { D() {} ~D() {} // 소멸자가 암시적 이동 할당을 방지함 D& operator=(D&&) = default; // 강제로 이동 할당 수행 }; int main() { A a1, a2; std::cout << "우측값 임시 객체로부터 A 이동 할당 시도\n"; a1 = f(A()); // 우측값 임시 객체로부터 이동 할당 std::cout << "xvalue로부터 A 이동 할당 시도\n"; a2 = std::move(a1); // xvalue로부터 이동 할당 std::cout << "\nB 이동 할당 시도\n"; B b1, b2; std::cout << "이동 전, b1.s = \"" << b1.s << "\"\n"; b2 = std::move(b1); // 암시적 이동 할당 호출 std::cout << "이동 후, b1.s = \"" << b1.s << "\"\n"; std::cout << "\nC 이동 할당 시도\n"; C c1, c2; c2 = std::move(c1); // 복사 할당 연산자 호출 std::cout << "\nD 이동 할당 시도\n"; D d1, d2; d2 = std::move(d1); }
출력:
우측값 임시 객체로부터 A 이동 할당 시도 move assigned xvalue로부터 A 이동 할당 시도 move assigned B 이동 할당 시도 이동 전, b1.s = "test" move assigned 이동 후, b1.s = "" C 이동 할당 시도 copy assigned D 이동 할당 시도 move assigned
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 수정된 동작 |
|---|---|---|---|
| CWG 1353 | C++11 |
기본 이동 대입 연산자가 삭제된 것으로 정의되는 조건에서
다차원 배열 타입을 고려하지 않음 |
해당 타입들을 고려함 |
| CWG 1402 | C++11 |
비트리비얼 복사 대입 연산자를 호출할 기본 이동 대입 연산자가
삭제됨; 삭제된 기본 이동 대입 연산자가 오버로드 해결에 여전히 참여함 |
해당 복사 대입 연산자 호출
허용; 오버로드 해결에서 무시되도록 변경 |
| CWG 1806 | C++11 |
가상 기본 클래스를 포함하는 기본 이동 대입 연산자에 대한
명세가 누락됨 |
추가됨 |
| CWG 2094 | C++11 |
volatile 서브오브젝트가 기본 이동 대입 연산자를
비트리비얼하게 만듦 ( CWG 이슈 496 ) |
트리비얼리티에 영향 없음 |
| CWG 2180 | C++11 |
클래스
T
에 대한 기본 이동 대입 연산자가
T
가 추상 클래스이고 이동 대입 불가능한 직접 가상
기본 클래스를 가질 경우 삭제된 것으로 정의되지 않음 |
이 경우 연산자가 삭제된
것으로 정의됨 |
| CWG 2595 | C++20 |
더 제약된 이동 대입 연산자가 존재하지만 해당 연관 제약을
만족하지 못할 경우 이동 대입 연산자가 적격이 되지 않음 |
이 경우 적격이 될 수 있음 |
| CWG 2690 | C++11 |
유니온 타입에 대한 암시적으로 정의된 이동 대입 연산자가
객체 표현을 복사하지 않음 |
객체 표현을 복사함 |