씹어먹는 C++
13장 우측값과 이동 연산 649p-670p
template <typename T>
void my_swap(T &a, T &b) {
T tmp(a);
a = b;
b = tmp;
}
swap 함수를 이용하여 임시 객처 생성 후 복사 복사
좌측값을 우측값으로 바꾸어주는 함수
인자로 받은 객체를 우측값으로 타입 변환만 수행하여 리턴
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를 통해서가 아니라
우측값을 받는 함수들이 오버로딩 되면서 수행되는 것
문맥에 따라 & L-value Reference, && R-value Reference 을 구분하지 못하는 경우
std::forward를 통해 타입을 분명하게 해줌
우측값 레퍼런스 일 때 에만 마치 move 를 적용한 것 처럼 작동
std::forward<T>(u)
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 -----
좌측값 레퍼런스 호출
좌측값 레퍼런스 호출
좌측값 레퍼런스 호출
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