이동 연산

SOEUN CHOI·2022년 6월 24일
0

C++_study

목록 보기
11/15

씹어먹는 C++

13장 우측값과 이동 연산 649p-670p


if lvalue를 이동 시키고 싶다면?

template <typename T>
void my_swap(T &a, T &b) {
T tmp(a);
a = b;
b = tmp;
}

swap 함수를 이용하여 임시 객처 생성 후 복사 복사

  • 즉, 불필요한 복사를 3번 진행
    굳이 복사할 필요 없이
    각 MyString 객체의 string_content 주소값만 서로 바꿔주면 해결 가능
    하지만 a 가 좌측값이라 이상태에서는 이동 생성자 오버로딩 불가능
    -> move 함수 사용하여 해결

Move 함수 (Move semantics)

좌측값을 우측값으로 바꾸어주는 함수
인자로 받은 객체를 우측값으로 타입 변환만 수행하여 리턴

  • <utility> library 사용

example code


#include <iostream>
#include <utility>

class A {
  public:
  A() { std::cout << "일반 생성자 호출!" << std::endl; }
  A(const A& a) { std::cout << "복사 생성자 호출!" << std::endl; }
  A(A&& a) { std::cout << "이동 생성자 호출!" << std::endl; }
};
int main() {
  A a;
  std::cout << "---------" << std::endl;
  A b(a);
  std::cout << "---------" << std::endl;
  A c(std::move(a));
}

일반 생성자 호출!
---------
복사 생성자 호출!
---------
이동 생성자 호출!

이동 연산자(operater =)을 사용한 swap
이동 생성이므로 복사 생성 보다 훨씬 빠르게 수행

template <typename T>
void my_swap(T &a, T &b) {
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}

이동 자체는 move를 통해서가 아니라
우측값을 받는 함수들이 오버로딩 되면서 수행되는 것

완벽한 전달 (perfect forwarding)

문맥에 따라 & L-value Reference, && R-value Reference 을 구분하지 못하는 경우
std::forward를 통해 타입을 분명하게 해줌
우측값 레퍼런스 일 때 에만 마치 move 를 적용한 것 처럼 작동
std::forward<T>(u)

  • 주의
    좌측값 레퍼런스일 때 move 를 해버린다면
    좌측값에 오버로딩 되는 g 가 아닌
    우측값에 오버로딩 되는 g 가 호출
    -> 이경우 forward 사용
    g(std::forward<T>(u));

구분을 못하는 경우

C++ 컴파일러가 템플릿 타입을 추론할 때,
템플릿 인자 T 가 레퍼런스가 아닌 일반적인 타입이라면
const 를 무시
즉, wrapper에서 T가 전부 class A 로 추론함
-> 세 경우 전부 다 좌측값 레퍼런스를 호출

example code

#include <iostream>
#include <vector>
template <typename T>
  void wrapper(T u) {
  g(u);
}
class A {};
void g(A& a) { std::cout << "좌측값 레퍼런스 호출" << std::endl; }
void g(const A& a) { std::cout << "좌측값 상수 레퍼런스 호출" << std::endl; }
void g(A&& a) { std::cout << "우측값 레퍼런스 호출" << std::endl; }

int main() {
  A a;
  const A ca;
  std::cout << "원본 --------" << std::endl;
  g(a);
  g(ca);
  g(A());
  std::cout << "Wrapper -----" << std::endl;
  wrapper(a);
  wrapper(ca);
  wrapper(A());
}

원본 --------
좌측값 레퍼런스 호출
좌측값 상수 레퍼런스 호출
우측값 레퍼런스 호출
Wrapper -----
좌측값 레퍼런스 호출
좌측값 레퍼런스 호출
좌측값 레퍼런스 호출

보편적 레퍼런스(Universal reference)

T&&
템플릿 인자 T에 대해서, 우측값 레퍼런스로 받는 형태
또한 다른 타입의 레퍼런스와 다르게 우측값 뿐만 아니라 좌측값도 받는 것도 가능

-> 레퍼런스 겹침 규칙 (reference collapsing rule) 에 따라 T 의 타입을 추론함

example code

#include <iostream>
#include <vector>

/* T&& 보편적 레퍼런스 
	forward 함수 사용 */
template <typename T>
void wrapper(T&& u) {
	g(std::forward<T>(u));
}

class A {};
void g(A& a) { std::cout << "좌측값 레퍼런스 호출" << std::endl; }
void g(const A& a) { std::cout << "좌측값 상수 레퍼런스 호출" << std::endl; }
void g(A&& a) { std::cout << "우측값 레퍼런스 호출" << std::endl; }

int main() {
  A a;
  const A ca;
  std::cout << "원본 --------" << std::endl;
  g(a);
  g(ca);
  g(A());
  std::cout << "Wrapper -----" << std::endl;
  wrapper(a);
  wrapper(ca);
  wrapper(A());
}

원본 --------
좌측값 레퍼런스 호출
좌측값 상수 레퍼런스 호출
우측값 레퍼런스 호출
Wrapper -----
좌측값 레퍼런스 호출
좌측값 상수 레퍼런스 호출
우측값 레퍼런스 호출

참고
https://ansohxxn.github.io/cpp/chapter19-7/
https://lakanto.tistory.com/46

profile
soeun choi

0개의 댓글