이 글은 개인의 학습을 목적으로 정리한 글입니다. 이점 참고하고 읽어주세요;)
#include <iostream>
#include <vector>
#include <utility>
using namespace std;
struct MyStruct
{};
void func(MyStruct& s)
{
cout << "Pass by L-ref" << endl;
}
void func(MyStruct&& s)
{
cout << "Pass by R-ref" << endl;
}
int main()
{
MyStruct s;
func(s); // L-value
func(MyStruct()); // R-value. 초기화 단계에서 생성되고 바로 다른 곳으로 넘어갈 운명?이기 때문에 R-value
return 0;
}
template<typename T> // 컴파일러가 템플리타이즈 함수는 L, R-value 분류를 못함
void func_wrapper(T t)
{
func(t);
}
int main()
{
MyStruct s;
func_wrapper(s);
func_wrapper(MyStruct());
return 0;
}
템플릿을 사용하는 함수에 대해서는 컴파일러가 L-value와 R-value를 구분하지 못함
template<typename T>
void func_wrapper(T&& t)
{
func(std::forward<T>(t));
// 전달 인자가 L-value로 들어오면 L-value로, R-value로 들어오면 R-value로 처리해줌
}
#include <iostream>
#include <vector>
#include <utility>
#include <utility>
using namespace std;
class CustomVector
{
public:
unsigned n_data = 0; // 벡터의 크기
int* ptr = nullptr; // 벡터가 저장될 주소
CustomVector(const unsigned& _n_data, const int& _init = 0)
{
cout << "Constructor" << endl;
init(_n_data, _init); // 생성자에서 직접 초기화를 하기보다는, 생성자 외부에 초기화 함수를 따로 두는 것이 더욱 편리함
}
CustomVector(CustomVector& l_input) // copy-constructor
{
cout << "Copy constructor" << endl;
init(l_input.n_data);
for (unsigned i = 0; i < n_data; ++i)
{
ptr[i] = l_input.ptr[i];
}
}
CustomVector(CustomVector&& r_input)
{
cout << "Move constructor" << endl;
n_data = r_input.n_data;
ptr = r_input.ptr;
r_input.n_data = 0;
r_input.ptr = nullptr; // 더 이상 필요가 없기 때문에 삭제
}
~CustomVector()
{
delete[] ptr;
}
void init(const unsigned& _n_data, const int& _init = 0)
{
n_data = _n_data;
ptr = new int[n_data];
for (unsigned i = 0; i < n_data; ++i) // for문으로 초기값을 받아와서 저장
ptr[i] = _init;
}
};
int main()
{
CustomVector my_vec(10, 1024);
CustomVector temp(my_vec); // L-value로 들어옴
cout << my_vec.n_data << endl; // 그대로 존재
}
L-value로 temp에 my_vec을 전달했기 때문에 my_vec의 n_data가 그대로 출력됨
int main()
{
CustomVector my_vec(10, 1024);
CustomVector temp(std::move(my_vec)); // R-value로 들어옴
cout << my_vec.n_data << endl; // my_vec의 값이 완전히 temp로 넘어가서 더 이상 존재 X
}
Move constructor를 통해 my_vec이 R-value로 temp에 넘어갔기 때문에 my_vec의 주소가 0으로 초기화되고 my_vec에 아무런 값이 남아있지 않음.
void doSomething(CustomVector& vec) // L-value로 매개변수를 받아옴
{
cout << "Pass by L-reference" << endl;
CustomVector new_vec(vec); // Copy-constructor
}
void doSomething(CustomVector&& vec) // R-value로 매개변수를 받아옴
{
cout << "Pass by R-value" << endl;
CustomVector new_vec(std::move(vec)); // Move-constructor
}
int main()
{
CustomVector my_vec(10, 1024);
doSomething(my_vec); // 이후에도 계속 사용할 계획 혹은 의지
cout << endl;
doSomething(CustomVector(10, 8)); // R-value로 doSomething을 호출
}
1. Constructor : my_vec(10, 1024) 초기화를 실행하면서 생성자 내부의 문자열 호출
2. doSomething(my_vec)을 실행하면서 L-value를 받는 doSomething 함수의 문자열 출력
3. doSomething 함수 내에서 전달인자로 받은 CustomVector 객체를 vec에 copy-construct하면서 출력
4. CustomeVector(10, 8)을 바로 생성하면서 생성자 내부의 문자열 호출
5. 4번에서 생성산 객체가 R-value로 전달되기 때문에 R-value를 받는 doSomething 함수가 실행되고 그 내부의 문자열 출력
6. doSomething 함수 내에서 std::move()를 통해 매개변수를 전달받아 move-construnct 생성자 내부의 문자열 출력
※ R-value 전달은 반드시 std::move를 사용해야 합니다
template<typename T>
void doSomethingTemplate(T vec)
{
doSomething(vec);
}
int main()
{
CustomVector my_vec(10, 1024);
doSomethingTemplate(my_vec);
doSomethingTemplate(CustomVector(10, 8));
}
클래스 역시 템플릿을 사용하면 컴파일러가 알아서 L-value 전달과 R-value 전달을 구분하지 못함
template<typename T>
void doSomethingTemplate(T&& vec) // std::forward 사용
{
doSomething(std::forward<T>(vec));
}
int main()
{
CustomVector my_vec(10, 1024);
doSomethingTemplate(my_vec);
doSomethingTemplate(CustomVector(10, 8)); // R-value reference로 전달
}
std::forward()를 사용함으로써 컴파일러가 스스로 L-value와 R-value를 구분해서 전달
'Information Technology > C++' 카테고리의 다른 글
[C++] 벡터 내적과 멀티 쓰레딩 (0) | 2020.01.03 |
---|---|
[C++] 작업 기반 비동기 프로그래밍 (0) | 2020.01.03 |
[C++] 레이스 컨디션 (0) | 2020.01.02 |
[C++] 멀티 쓰레딩 기초 (0) | 2020.01.01 |
[C++] C++17에서 여러 개의 리턴값 반환 (0) | 2020.01.01 |