50
Move Semantics 쿠재아이 김재경 아주 짤막하게 알아봅시다…

Move semantics

Embed Size (px)

Citation preview

Page 1: Move semantics

Move Semantics

쿠재아이

김재경

아주 짤막하게 알아봅시다…

Page 2: Move semantics

☆도입된 이유☆

(기존에 있던 문제점)

Page 3: Move semantics

Vector의 동적 크기 증가

평범 평범한 코드

Page 4: Move semantics

Vector의 동적 크기 증가

음… 뭔가 이상한데?

Page 5: Move semantics

Vector의 동적 크기 증가

Vector

배열 기반 컨테이너라 크기를 증가시키기 위해서 재할당이 필수

그래서 재할당 과정을 보면…

Page 6: Move semantics

Vector의 동적 크기 증가

v a Int[1000]

1. 크기가 더 큰 새로운 Vector를 만든다

Page 7: Move semantics

Vector의 동적 크기 증가

v a Int[1000]

2. 이곳에 기존 Vector에 있던 원소들을 모두 복사한다(복사 생성자 호출, Deep Copy 발생)

Int[1000]

a

Page 8: Move semantics

Vector의 동적 크기 증가

v

해제된 공간

3. 기존 벡터안에 있던 원소들을 모두 해제한다(소멸자 호출)

Int[1000]

a

Page 9: Move semantics

Vector의 동적 크기 증가

v

4. 새로운 벡터를 기존 벡터 이름에 연결한다

Int[1000]

a

Page 10: Move semantics

Vector의 동적 크기 증가

1. createMyVec() 은 내부에서 임시로 비어있는 MyVec 객체를 하나 만들고 (vec2라 가정)2. createMyVec() 함수 안에서 정의된 일을 수행한 후,3. vec2를 vec 에 대입

3번 과정의 내부를 좀 더 들여다 봅시다.3-1) createMyVec() 함수가 반환할 때 우선 넘겨받아야 할 vec 에 빈 자리가 있어야 하므로,3-2) vec이 가지고 있던 엘리먼트들을 파괴합니다. vec 안에 무엇이 들어있던 간에 파괴됩니다. 3-3) vec 내부 확보가 끝나면, vec2 내의 엘리먼트를 vec 으로 하나 하나 복사합니다.3-4) 복사를 모두 마치면, vec2는 소멸되어야 하므로, vec2 내부에 들어있는 엘리먼트를 파괴합니다.3-5) vec2 의 엘리먼트들이 모두 소멸되고 난 후에, vec2를 소멸합니다

Page 11: Move semantics

정말 비효율적이네요 ^^

Page 12: Move semantics

이런 문제를 해결하기 위해 도입된 것이

Move Semantics

이동을 하고 싶은데 복사를 해야 돼…

Page 13: Move semantics

Move Semantics를 알기 전에 알아야 할 개념

R-Value Reference

Page 14: Move semantics

L-Value, R-Value?

C 표준에서는 이 둘을 아래와 같이 정의

An L-Value is an expression that may appear on the left or on the right hand side of an assignment, whereas an R-Value is an expression that can only appear on the right hand side of an assignment

대입식 (assignment)에서 왼쪽 또는 오른쪽에 나올 수 있는게 L-Value (left-hand side value의 약자)오른쪽에만 나올 수 있는게 R-Value (right-hand side value의 약자)

Page 15: Move semantics

L-Value, R-Value?

L-Value어떤 값을 저장할 수 있고, & 연산자를 통해 해당 메모리의 주소값을 가져올 수 있는 표현식

R-Value값을 저장할 수 없으며, L-Value 에 값을 저장하기 위해 임시로 생성된 표현식& 연산자를 써서 주소값을 가져오는 것도 불가능

Page 16: Move semantics

L-Value, R-Value?

C++에서 레퍼런스 타입을 반환하는 함수에 대한 호출은 L-Value를 반환하고, 이를 제외한 함수는 모두 R-Value를 반환

foo() 함수 호출은 L-Value를 반환하고, L-Value는 & 연산자를 통해 주소값을가져올 수 있다

Page 17: Move semantics

R-Value Reference?

L-Value 레퍼런스는 우측값으로 L-Value만 받을 수 있다(const L-Value 레퍼런스는 L-Value, R-Value 둘 다 가능)

R-Value 레퍼런스는 우측값으로 R-Value만 받을 수 있다

Page 18: Move semantics

R-Value Reference?

Page 19: Move semantics

R-Value Reference를 써보자

메모리 이동을 하기 위해Shallow Copy 구현

Shallow Copy를 구현 했기때문에 other.p에 널 포인터를대입

Page 20: Move semantics

R-Value Reference를 써보자

Vector가 재할당 될 때 메모리가 생성되지 않는다!

Page 21: Move semantics

R-Value Reference를 써보자

a와 b를 push 후 더 이상 사용하지 않는다면 이렇게도 가능

메모리 할당이 객체가 생성될 때만 일어났다

Page 22: Move semantics

std::move

인자를 R-Value Reference로 리턴

전 코드는 Move 생성자를 호출하기 위해 사용

Page 23: Move semantics

Vector의 동적 크기 증가(Move 생성자 사용)

v a Int[1000]

1. 크기가 더 큰 새로운 Vector를 만든다

Page 24: Move semantics

v

nullptr

a Int[1000]

2. 이곳에 기존 Vector에 있던 원소들을 모두 이동시킨다(Move 생성자 호출, Shallow Copy 발생)

a

Vector의 동적 크기 증가(Move 생성자 사용)

Page 25: Move semantics

v

해제된 공간

Int[1000]

3. 기존 벡터안에 있던 원소들을 모두 해제한다(소멸자 호출)

a

Vector의 동적 크기 증가(Move 생성자 사용)

Page 26: Move semantics

Vector의 동적 크기 증가

4. 새로운 벡터를 기존 벡터 이름에 연결한다

v Int[1000]

a

Page 27: Move semantics

C++ 탄생 28년 만에 Ctrl+X 탄생

Page 28: Move semantics

Vector의 동적 크기 증가

Vector 내부에 R-Value&&로 연산자가 오버로딩 되어 있는걸로 보아

vec의 원소를 해제하고 내부의 포인터를 이동시키지 않을까…?

Page 29: Move semantics

멍청했던 점

Move 생성자는 복사 생성자와 다른 특별한 생성자가 아니다

메모리 이동을 하기 위해 생성자를 하나 더 추가한 것뿐

그래서 복사 생성자와 Move 생성자를 똑같이 만들면

Page 30: Move semantics

멍청했던 점

복사 생성자와

Page 31: Move semantics

멍청했던 점

Move 생성자의 구분이 없어진다상식적으로 코드가 똑같은데 당연한 결과…

Page 32: Move semantics

한 가지 궁금점

생성자와 복사 생성자는 컴파일러가 default로 만들어주는데…

Move 생성자는?

Page 33: Move semantics

암묵적인 Move 생성자 정의 이슈

Page 34: Move semantics

기타 레퍼런스 정보들

Page 35: Move semantics

Perfect Forwarding이것도 사전지식이 좀 필요하다

Page 36: Move semantics

MSDN R-Value Reference 부분을 보면…

The compiler treats a named rvalue reference as an lvalue and an unnamed rvalue reference as an rvalue

컴파일러는 이름이 있는 R-Value Reference를 L-Value로, 이름이 없는 R-Value Reference를 R-Value로 처리합니다.

R-Value Reference 처리

Page 37: Move semantics

R-Value Reference 처리

f 함수에서 block은 named rvalue reference따라서 함수 내부에서는 R-Value로 처리

f 함수는 unnamed rvalue reference를 리턴

왜인지는 저도 몰라요…

Page 38: Move semantics

Argument Deduction

R-Value Reference에 L-Value를 대입할 수 없다

Page 39: Move semantics

하지만 템플릿을 사용하면 R-Value Reference에 L-Value를 대입할 수 있다

Argument Deduction

Page 40: Move semantics

Argument Deduction

템플릿은 컴파일 타임에 함수를 생성

이 때 Argument Deduction(인자 추론)을 진행

그런 다음 컴파일러는 매개 변수 형식을 추론된 템플릿 인자로 대체할 때reference collapsing rules를 적용

Argument Deduction

reference collapsing rules 적용

Page 41: Move semantics

std::move

전달 인자가 L-Value면 _Ty&&이 L-Value Reference로 바뀌고

전달 인자가 R-Value면 함수 내부적으로 L-Value로 처리되기 때문에타입 캐스팅을 해줘서 리턴

리턴한 이름없는 R-Value Reference를 R-Value로 사용

Page 42: Move semantics

Argument Deduction

PrintRefValue의 Value가 내부에서 L-Value로처리되기 때문

Page 43: Move semantics

Argument Deduction

L-Value로 전달한 것도 R-Value Reference로실행

함수 호출시 좌측값과 우측값을 구분하여 처리할 수 있는 방법은 없나?

Page 44: Move semantics

std::forward

std::forward는 템플릿 프로그래밍에서 사용

원래 L-Value인 것은 L-Value Reference로

원래 R-Value인 것은 R-Value Reference로

캐스팅해주는 역할을 한다

Page 45: Move semantics

std::forward

결과적으로 L-Value Reference를 리턴

printRefValue(nValue)일 때

Argument Deduction

Page 46: Move semantics

std::forward

결과적으로 R-Value Reference를 리턴

printRefValue(323)일 때

Argument Deduction

Page 47: Move semantics

Perfect Forwarding

factory 함수의 인자가 int&, int&으로 만들어져R-Value를 받는 Z를 생성 못함

Z를 생성하려면 오버로딩 해야 함

Page 48: Move semantics

Perfect Forwarding

템플릿 함수 인자로 R-Value Reference를 설정하고

std::forward 함수를 이용해 해결

Page 49: Move semantics

결론

신기술이 도입되면

1. 이전에 어떤 문제점이 있는지 파악하고

2. 문제를 해결하기 위해 어떤 개념을 사용했으며

3. 결과적으로 어떤 의미로 도입이 됐는지 생각하고

4. 구현

5. 영어 공부 합시다…

6. 백업 잘 합시다…

Page 50: Move semantics

Q/A