문서의 임의 삭제는 제재 대상으로, 문서를 삭제하려면 삭제 토론을 진행해야 합니다. 문서 보기문서 삭제토론 C++/문법 (문단 편집) ==== 완벽한 매개변수 전달 ==== {{{#!syntax cpp import ; void ValueFunction(auto value); void LvalueFunction(auto& value); void RvalueFunction(auto&& value); auto&& Function4_forwarding(auto&& value) { // 복사, &, &&, []가 모두 사라짐 (Decay) // 복사할 수 없는 값이라면 오류 발생함 return value; // std::move는 lvalue를 보존하지 않기 때문에 문제가 생긴다. // value가 glvalue // T&: T&& // const T&: lvalue는 const T&, xvalue는 const T&& // value가 rvalue // T&& - T&& // const T&& - const T&& return std::move(value); } int main() { const long A = 132435; ValueFunction(A); // value는 long ValueFunction(std::move(A)); // value는 long ValueFunction(8000); // 리터럴 value는 int LvalueFunction(A); // value&는 const long& LvalueFunction(std::move(A)); // value&는 const long& LvalueFunction(8000); // 오류! 리터럴은 lvalue에 대입할 수 없음 RvalueFunction(A); // value&&는 const long& RvalueFunction(std::move(A)); // value&&는 const long&& RvalueFunction(8000); // 리터럴 value&&는 int&& } }}}'''완벽한 전달(Perfect Forwarding)''' {{{auto}}}는 인자의 자료형을 썩히고(Decay), {{{*}}} 혹은 순수한 자료형만 보존한다. 즉 {{{const}}}, {{{volatile}}}, {{{&}}}, {{{&&}}}는 무시하고 값으로 전달을 시행한다. 왜냐하면 썩힌다는 것은 최소한의 의미만 남기고 자료형을 날린다는 것인데, 단일 {{{const}}}, {{{volatile}}}은 함수에 전달된 이상 아무 의미가 없기 때문이다. 참조형이 아니라면 그게 상수던 휘발성이던 값으로 전달될 것이고, 그럼 복사가 되든 이동이 되든지 간에, 인자로 전달된 순간부터는 함수 안에서 밖으로 영향을 끼치지 못한다. 사용자 단에서도 {{{const}}}는 단지 코딩에서 실수를 줄이거나 모호함을 줄이기 위해 구태여 붙이는 한정자이지, 인자로 전달됐던 원본 값이랑은 전혀 연관이 없는 변수가 된다. {{{const}}}, {{{volatile}}}, {{{&}}}, {{{&&}}}은 서로 보완하지 않으면 함수 안에서는 아무 의미를 갖지 못한다. 그래서 사용자가 {{{auto&}}}로 지정하면 {{{&}}}에 의존하는 모든 한정자가 딸려나온다. 굳이 {{{const volatile}}}을 붙이지 않아도 말이다. 그러나 {{{const}}}또는 {{{volatile}}}가 없는 {{{auto&}}}는 무조건 {{{lvalue}}}가 되어서 {{{&&}}}로 표현되는 리터럴과 임시값을 넣을 수 없다. 예를 들어서 예제의 {{{LvalueFunction}}}에는 {{{500}}}, {{{int(120648395)}}}같은 값을 전달할 수 없다. 그럼 좌측값, 우측값 매개변수 구분을 위해 {{{const&}}}, {{{&&}}}를 모두 오버로딩해야만 할까? 사실 그렇지 않다. 가령 예제의 {{{RvalueFunction}}} 함수는 {{{rvalue}}}만 받을 수 있을 것 같지만, {{{auto&&}}}는 모든 한정자에 대해 사용할 수 있다. {{{#!syntax cpp import ; template T&& Function5_forwarding_by_template(T&& value) noexcept(noexcept(std::declval())) // 원본 자료형을 유지한채 value의 객체가 생성될 때 예외가 없음을 확인한다 { // lvalue, xvalue, prvalue 모두가 원래 값 범주(Value Category)를 유지한채, 아무 비용없이 전달된다 // lvalue는 lvalue 그대로 전달된다 // xvalue를 감싸 이름없이 전달한다 // prvalue를 감싸 이름없이 전달한다 return std::forward(value); } template auto&& Function5_modified_forwarding_by_template(V&& value) noexcept(noexcept(std::forward_like(std::declval()))) // 원본 자료형을 바꾼 value의 객체가 생성될 때 예외가 없음을 확인한다 { // 원래 값 범주를 유지한채로, 다른 자료형으로 바꿔 전달할 수 있다. return std::forward_like(value); } // C++23부터 사용할 수 있는 Function5_forwarding_by_template과 같은 코드 auto&& Function5_forwarding_by_deduction(auto&& value) noexcept(noexcept(std::declval())) // 원본 자료형 그대로 value의 객체가 생성될 때 예외가 없음을 확인한다 { // C++23부터 가능한 완벽한 전달 수단 return auto{ value }; // 또는 return auto(value); } Position Function6_forwarding_by_copy(const Position& pos) noexcept(std::is_nothrow_copy_constructible) { // pos를 복사해서 전달한다 return pos; } Position&& Function6_forwarding_by_move(Position&& pos) noexcept(std::is_nothrow_move_constructible) { // pos를 아무 성능 오버헤드 없이 그대로 전달한다 return std::move(pos); // 이동 연산에 써도 문제 없다. 그러나 lvalue가 아님을 유의해야 한다 return std::forward(pos); // 경고! 이 경우 복사가 되어 참조 Dangling이 일어난다 return pos; } }}} 매개변수로 실체화하기 전, 인자에서는 {{{&}}}, {{{&&}}}를 멀쩡하게 갖고 있다. 그리고 이때 &가 여러번 중첩될 경우 {{{&}}} 또는 {{{&&}}} 중 한 가지 경우로 압축한다. {{{&}}}는 {{{&&}}}앞에 있으면 {{{&&}}}가 되어버린다. 다시 말해서 {{{static_cast(T&)}}}는 {{{T&&}}}로 연역된다. {{{static_cast(T&)}}}는 {{{const T&&}}}로 연역된다. 이 특성은 특이하게도 매개변수의 원본 자료형을 그대로 보존하는 효과가 나온다. 덕분에 {{{auto}}}에서 원본 자료형이 뭔지 알기 위해 {{{decltype(auto)}}}을 쓸 필요가 없다. 그리고 {{{const auto&}}}, {{{auto&&}}}를 모두 오버로딩 할 필요가 없다. 매개변수가 뭔지, 복사해야 할지 참조해야 할지 이동시켜야 할지 고민할 필요를 없애준다.저장 버튼을 클릭하면 당신이 기여한 내용을 CC-BY-NC-SA 2.0 KR으로 배포하고,기여한 문서에 대한 하이퍼링크나 URL을 이용하여 저작자 표시를 하는 것으로 충분하다는 데 동의하는 것입니다.이 동의는 철회할 수 없습니다.캡챠저장미리보기