`= default` 이동 생성자는 멤버 단위 이동 생성자와 동일합니까?
이것은
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) : a{mE.a}, b{mE.b} { }
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
Example& operator=(const Example& mE) { a = mE.a; b = mE.b; return *this; }
Example& operator=(Example&& mE) { a = move(mE.a); b = move(mE.b); return *this; }
}
이것에 해당
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) = default;
Example(Example&& mE) = default;
Example& operator=(const Example& mE) = default;
Example& operator=(Example&& mE) = default;
}
?
예, 둘 다 동일합니다.
그러나
struct Example {
int a, b;
Example(int mA, int mB) : a{mA}, b{mB} { }
Example(const Example& mE) = default;
Example(Example&& mE) = default;
Example& operator=(const Example& mE) = default;
Example& operator=(Example&& mE) = default;
}
이 버전에서는 본문 정의를 건너 뛸 수 있습니다.
그러나 다음을 선언 할 때 몇 가지 규칙을 따라야합니다 explicitly-defaulted-functions
.
8.4.2 명시 적으로 기본값이 지정된 함수 [dcl.fct.def.default]
다음 형식의 함수 정의 :
attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt = default ;
명시 적 기본값 정의 라고합니다 . 명시 적으로 기본값이 설정된 함수는
특별한 멤버 함수 여야합니다.
선언 된 함수 유형이 동일합니다 (다른 ref 한정자 를 제외하고 복사 생성자 또는 복사 할당 연산자의 경우 매개 변수 유형은 "비상 수에 대한 참조"일 수 있음을 제외하고
T
, 여기서는T
멤버 함수의 이름입니다). class) 암시 적으로 선언 된 것처럼기본 인수가 없습니다.
예, 기본 이동 생성자는 기본 및 멤버의 멤버 별 이동을 수행하므로 다음과 같습니다.
Example(Example&& mE) : a{move(mE.a)}, b{move(mE.b)} { }
다음과 같습니다.
Example(Example&& mE) = default;
우리가로 이동하여 볼 수 있습니다 초안 C ++ 11 표준 섹션 12.8
클래스를 복사 및 이동이 객체 문단 13 (라고 강조 광산 향후 ) :
기본값으로 설정되고 삭제 된 것으로 정의되지 않은 복사 / 이동 생성자 는 odrused (3.2)이거나 첫 번째 선언 이후에 명시 적으로 기본값이 지정 될 때 암시 적으로 정의 됩니다. [참고 : 복사 / 이동 생성자는 구현에서 odr-use (3.2, 12.2)를 제거하더라도 암시 적으로 정의됩니다. —end note] [...]
그리고 다음과 같은 단락 15 :
암시 적으로 정의 복사 / 이동 생성자 비 노조 클래스 X에 대한 수행의 기초 및 회원의 memberwise의 복사 / 이동 . [참고 : 비 정적 데이터 멤버의 중괄호 또는 같음 이니셜 라이저는 무시됩니다. 12.6.2의 예도 참조하십시오. —end note] 초기화 순서는 사용자 정의 생성자의 기본 및 멤버 초기화 순서와 동일합니다 (12.6.2 참조). x를 생성자의 매개 변수이거나 이동 생성자의 경우 매개 변수를 참조하는 xvalue가되도록하십시오. 각 기본 또는 비 정적 데이터 멤버는 해당 유형에 적합한 방식으로 복사 / 이동됩니다.
- 멤버가 배열이면 각 요소는 x의 해당 하위 객체로 직접 초기화됩니다.
- 멤버 m에 rvalue 참조 유형 T &&가 있으면 static_cast (xm)로 직접 초기화됩니다.
- otherwise, the base or member is direct-initialized with the corresponding base or member of x.
Virtual base class subobjects shall be initialized only once by the implicitly-defined copy/move constructor (see 12.6.2).
Is a
=default
move constructor equivalent to a member-wise move constructor?
Yes. Update: Well, not always. Look at this example:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) = default;
nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "copy" << std::endl; }
movable( movable &&) { std::cerr << "move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c)); // prints copy
}
It prints:
copy
http://coliru.stacked-crooked.com/a/62c0a0aaec15b0eb
You declared defaulted move constructor, but copying happens instead of moving. Why? Because if a class has even a single non-movable member then the explicitly defaulted move constructor is implicitly deleted (such a pun). So when you run has_nonmovable d = std::move(c)
, the copy constructor is actually called, because the move constructor of has_nonmovable
is deleted (implicitly), it just doesn't exists (even though you explicitly declared the move constructor by expression has_nonmovable(has_nonmovable &&) = default
).
But if the move constructor of non_movable
was not declared at all, the move constructor would be used for movable
(and for every member that has the move constructor) and the copy constructor would be used for nonmovable
(and for every member that does not define the move constructor). See the example:
#include <iostream>
struct nonmovable
{
nonmovable() = default;
nonmovable(const nonmovable &) { std::cerr << "nonmovable::copy" << std::endl; }
//nonmovable( nonmovable &&) = delete;
};
struct movable
{
movable() = default;
movable(const movable &) { std::cerr << "movable::copy" << std::endl; }
movable( movable &&) { std::cerr << "movable::move" << std::endl; }
};
struct has_nonmovable
{
movable a;
nonmovable b;
has_nonmovable() = default;
has_nonmovable(const has_nonmovable &) = default;
has_nonmovable( has_nonmovable &&) = default;
};
int main()
{
has_nonmovable c;
has_nonmovable d(std::move(c));
}
It prints:
movable::move
nonmovable::copy
http://coliru.stacked-crooked.com/a/420cc6c80ddac407
Update: But if you comment out the line has_nonmovable(has_nonmovable &&) = default;
, then copy will be used for both members: http://coliru.stacked-crooked.com/a/171fd0ce335327cd - prints:
movable::copy
nonmovable::copy
So probably putting =default
everywhere still makes sense. It doesn't mean that your move expressions will always move, but it makes chances of this higher.
One more update: But if comment out the line has_nonmovable(const has_nonmovable &) = default;
either, then the result will be:
movable::move
nonmovable::copy
So if you want to know what happens in your program, just do everything by yourself :sigh:
apart very pathological cases ... YES.
To be more precise, you have also to considered eventual bases Example
may have, with exact same rules. First the bases -in declaration order- then the members, always in declaration order.
'programing' 카테고리의 다른 글
문자열 보간과 문자열 형식 (0) | 2020.09.10 |
---|---|
Boost.Log 로깅 라이브러리 사용 경험이 있으십니까? (0) | 2020.09.10 |
setter가없는 속성이 직렬화되지 않는 이유 (0) | 2020.09.10 |
Java 프로젝트에서 패키지 이름 지정에 어떤 전략을 사용하며 그 이유는 무엇입니까? (0) | 2020.09.10 |
매개 변수와 함께 함수에 대한 참조를 어떻게 전달할 수 있습니까? (0) | 2020.09.10 |