Rvalue reference 강기완. string s0("my mother told me that"); string s1("cute"); string...
-
Upload
whitney-mckinney -
Category
Documents
-
view
223 -
download
2
Transcript of Rvalue reference 강기완. string s0("my mother told me that"); string s1("cute"); string...
string s0("my mother told me that");string s1("cute");string s2("fluffy");string s3("kittens");string s4("are an essential part of a healthy diet");
string dest = s0 + " " + s1 + " " + s2 + " " + s3 + " " + s4;
기존 operator+() x 8• 임시변수 생성 , reserve()• append(left), append(right)
lvalue 와 rvalue 를 구분하는 방법
“ 그 값의 주소를 얻을 수 있는가 ?”
주소를 얻을 수 있다면 lvalue
&obj, &*ptr, &ptr[index], &++x
&1729, &(x + y), &std::string(“meow”), &x++
함수 호출
string& lvalue() { ... }string rvalue() { ... }
함수가 값을 리턴하면 “호출”이 rvalue,
reference 를 리턴하면 lvalue
string one("cute");const string two("fluffy");string three() { ... }const string four() { ... } one; // modifiable lvaluetwo; // const lvaluethree(); // modifiable rvaluefour(); // const rvalue
void foo(string& str) {}void bar(const string& str) {}
foo(one);//foo(two); // 에러발생foo(three()); // 원래 C++ 은 이것을 허용하지 않지만 VC 는 이것을 허용한다 . Warning level4 에서 warning 발생//foo(four()); // 에러발생bar(one);bar(two);bar(three());bar(four());
string modifiable_lvalue("kittens");const string const_lvalue("hungry hungry zombies");string modifiable_rvalue() { ... }const string const_rvalue() { ... } string& a = modifiable_lvalue;string& b = const_lvalue; // ERRORstring& c = modifiable_rvalue(); // warning (VC)string& d = const_rvalue(); // ERROR
const string& e = modifiable_lvalue;const string& f = const_lvalue;const string& g = modifiable_rvalue();const string& h = const_rvalue();
warning C4239: nonstandard extension used : 'initial-izing' : conversion from 'std::string' to 'std::string &'
string modifiable_lvalue("kittens");const string const_lvalue("hungry hungry zombies");string modifiable_rvalue() { ... }const string const_rvalue() { ... } string&& i = static_cast<string&&>(modifiable_lvalue);string&& j = const_lvalue; // ERRORstring&& k = modifiable_rvalue();string&& l = const_rvalue(); // ERROR const string&& m = static_cast<const string&&>(modifiable_lvalue);const string&& n = static_cast<const string&&>(const_lvalue);const string&& o = modifiable_rvalue();const string&& p = const_rvalue();
void meow(string& s) { cout << "meow(string&): " << s << endl;} void meow(const string& s) { cout << "meow(const string&): " << s << endl;} void meow(string&& s) { cout << "meow(string&&): " << s << endl;} void meow(const string&& s) { cout << "meow(const string&&): " << s << endl;} string rvalue() { return “rvalue()”; } const string const_rvalue() { return “const_rvalue()”; }
int main() { string lvalue(“lvalue"); const string const_lvalue(“const_lvalue"); meow(lvalue); meow(const_lvalue); meow(rvalue()); meow(const_rvalue());} C:\Temp>cl /EHsc /nologo /W4 four_overloads.cppfour_overloads.cpp C:\Temp>four_overloadsmeow(string&): lvaluemeow(const string&): const_lvaluemeow(string&&): rvalue()meow(const string&&): const_rvalue()
void purr(const string& s) { cout << "purr(const string&): " << s << endl;} void purr(string&& s) { cout << "purr(string&&): " << s << endl;} string rvalue() { return “rvalue()”; } const string const_rvalue() { return “const_rvalue()”; }
int main() { string lvalue(“lvalue"); const string const_lvalue(“const_lvalue"); purr(lvalue); purr(const_lvalue); purr(rvalue()); purr(const_rvalue());} C:\Temp>cl /EHsc /nologo /W4 two_overloads.cpptwo_overloads.cpp C:\Temp>two_overloadspurr(const string&): lvaluepurr(const string&): const_lvaluepurr(string&&): rvalue()purr(const string&): const_rvalue()
overload resolution 기본 규칙
const correctnesslvalue 는 lvalue reference 로rvalue 는 rvalue reference 로 바인딩 하려는 성향이 있다 .( 강함 )
modifiable 은 modifiable reference 로 바인딩 하려는 성향이 있다 . ( 약함 )
class remote_integer {public: remote_integer() { cout << "Default constructor." << endl; m_p = NULL; } explicit remote_integer(const int n) { cout << "Unary constructor." << endl; m_p = new int(n); } remote_integer(const remote_integer& other) { cout << "Copy constructor." << endl; if (other.m_p) { m_p = new int(*other.m_p); } else { m_p = NULL; } }
remote_integer(remote_integer&& other) { cout << "MOVE CONSTRUCTOR." << endl; m_p = other.m_p; other.m_p = NULL; }
int get() const { return m_p ? *m_p : 0; } private: int * m_p;
move 생성자와 move 대입 연산자는 자동으로 생성되지 않는다 !
remote_integer& operator=(const remote_integer& other) { cout << "Copy assignment operator." << endl; if (this != &other) { delete m_p; if (other.m_p) { m_p = new int(*other.m_p); } else { m_p = NULL; } } return *this; } remote_integer& operator=(remote_integer&& other) { cout << "MOVE ASSIGNMENT OPERATOR." << endl; if (this != &other) { delete m_p; m_p = other.m_p; other.m_p = NULL; } return *this; }
const remote_integer&& 를 사용할 수 없음을 알아두자 !
int main() { remote_integer a(8); remote_integer b(10); b = square(a);}
MOVE
대입
연산
자 remote_integer square(const remote_integer& r) { const int i = r.get(); return remote_integer(i * i);}
잠깐 ! square() 가 const remote_integer 를 리턴한다면 ?move 대입 연산자를 사용하지 않는다 .
암시적 기본 생성자사용자 선언 생성자 :• 복사 생성자• Move 생성자
억제
class B {public: //B(const B& b) {} B(B&& b) {}};
B b; 기본 생성자가 억제되므로 컴파일 에러 !
Move 생성자는 복사 생성자를 억제하지 못한다 .Move 대입 연산자는 기본 대입 연산자를 억제하지 못한다 .
remote_integer(remote_integer&& other) { cout << "MOVE CONSTRUCTOR." << endl; m_p = other.m_p; other.m_p = NULL; }
remote_integer(remote_integer&& other) { cout << "MOVE CONSTRUCTOR." << endl; m_p = NULL; *this = other; // WRONG }
remote_integer(remote_integer&& other) { cout << "MOVE CONSTRUCTOR." << endl; m_p = NULL; *this = std::move(other); // RIGHT }
template <typename T> struct RemoveReference { typedef T type;}; template <typename T> struct RemoveReference<T&> { typedef T type;}; template <typename T> struct RemoveReference<T&&> { typedef T type;}; template <typename T> typename RemoveReference<T>::type&& Move(T&& t) { return t;}
lvalue 값이 더 이상 필요하지 않을 때 , move semantics 를 활성화 하기 위해 std::move(lvalue 표현식 ) 을 사용할 수 있다 .std::move() 는 상수성 (constness) 을 유지하면서 lvalue 를 rvalue 로 바꿔준다 .
template <typename T> void outer(T& t) { inner(t);}
outer(5); // ERROR
outer<const int>(5); // OK
const int i = 5;outer(i) // OK: T = const int
그런데 , inner(const int&) 이라면 ?
template <typename T> void outer(T& t) { inner(t);}
template <typename T> void outer(const T& t) { inner(t);}
단항 함수에 인자를 전달하려면 두 개의 함수 템플릿이 필요하다 .
template <typename T> void outer(...) { inner(...);}
매개변수가 여러 개라면 그 수의 지수만큼의 오버로드 함수가 있어야 한다 .
VC9 SP1 의 tr1::bind() 는 5 개의 인자에 대해 63 개의 오버로드를 만든다 .
template <typename T> struct Identity { typedef T type;}; template <typename T> T&& Forward(typename Identity<T>::type&& t) { return t;} void inner(int&, const int&) { cout << "inner(int&, const int&)" << endl;} void inner(const int&, int&) { cout << "inner(const int&, int&)" << endl;} template <typename T1, typename T2> void outer(T1&& t1, T2&& t2) { inner(Forward<T1>(t1), Forward<T2>(t2));}
이름 있는 rvalue reference 는 lvalue 이므로std::forward() 를 사용해야 한다 .std::forward() 는 타입을 보존하면서 이름을 없애준다 .
Identity 는 Forward() 에서 템플릿 인자 추론을못하게 막는다 . Identity 가 없다면 Forward() 의 T 가Type& 로 추론되어 Type& 을 리턴할 수 있다 .
스페샬 ~ 룰 !
C++11 에서 추론 방식이 좀 바뀌었다 .아마 ~ rvalue reference 때문 ?
template <typename T> void quark(T&& t);
string a; // lvalueconst string b; // lvaluequark(a);quark(b);
andT (const) string&
요런 경우엔 ‘스페샬 룰’이 적용 되지 않는다 .
template <typename T> void quark(T& t);template <typename T> void quark(const T& t);
template <typename T> void quark(const T&& t);
참조자 축소
T 가 string& 면 T&& 는 ‘ string& &&’ 인데 ,이건 어떻게 ?
template <typename T> void quark(T&& t);
string a; // lvaluequark(a);
X& &&, X&& &, X& & X&X&& && X&&
template <typename T> void quark(T&& t) { Name<T&&>::get();}
T string& 라면 T&& 는 ‘ string& &&’ 이 되고 ,참조자가 축소되어 ‘ string&’ 으로 바뀐다 .결국 , Name<string&>::get() 이 수행된다 .
T string&Name<string&>::g
et()
template <typename T>T&& forward(typename Identity<T>::type&& t);
template <typename T> void outer(T&& t) { inner(forward<T>(t));}
outer arg(string)
T T&&
lvalue string& string&
const lvalue const string& const string&
rvalue string string&&
const rvalue const string const string&&