가장 중요한 질문 우선 :
C ++로 매개 변수를 보내는 방법에 대한 모범 사례가 있습니까?
함수 가 전달되는 원래 객체 를 수정 해야하는 경우 호출이 반환 된 후 해당 객체에 대한 수정 사항이 호출자에게 표시되도록하려면 lvalue 참조로 전달해야합니다 .
void foo(my_class& obj)
{
// Modify obj here...
}
함수 가 원본 객체를 수정할 필요가없고 복사본을 만들 필요가없는 경우 (즉, 상태를 관찰하기 만하면const 됨) lvalue 참조를에 전달해야 합니다 .
void foo(my_class const& obj)
{
// Observe obj here
}
이렇게하면 lvalue (lvalue는 안정적인 ID를 가진 객체)와 rvalue (예 : temporaries 또는 호출 결과로 이동하려는 객체)를 사용하여 함수를 호출 할 수 std::move()있습니다.
하나는 주장 할 수 복사가 빠른있는 기본 유형 또는 유형 등, int, bool, 또는 char, 함수가 단순히 값을 관찰 할 필요가 있으며, 경우에 참조로 전달할 필요가 없습니다 값으로 전달하는 것이 선호한다가 . 그 경우 올바른 참조 시멘틱스가 필요하지 않습니다,하지만 기능은 미래의 다른 부분에 수행 된 값 수정을 볼 것이다 포인터를 통해 읽기 때문에, 바로 그 같은 입력 오브젝트 곳의 포인터를 저장하기를 원한다면 암호? 이 경우 참조로 전달하는 것이 올바른 솔루션입니다.
함수가있는 경우 원래 객체를 수정해야하지만, 그 객체의 복사본을 저장하는 데 필요하지 않습니다 ( 아마도 입력을 변경하지 않고 입력 변환의 결과를 반환하는 다음 고려할 수) 값으로 복용 :
void foo(my_class obj) // One copy or one move here, but not working on
// the original object...
{
// Working on obj...
// Possibly move from obj if the result has to be stored somewhere...
}
위의 함수를 호출하면 lvalue를 전달할 때 항상 하나의 사본이 생성되고 rvalue를 전달할 때 하나가 이동합니다. 함수가이 오브젝트 어딘가에 저장해야하는 경우, 당신은 추가로 수행 할 수 이동 그것에서을 (예를 들어, 경우 foo()입니다 필요가 데이터 멤버의 값을 저장하는 멤버 함수 ).
이동이 유형의 객체에 대해 비용이 많이 드는 경우my_class 오버로딩을 고려 foo()하고 lvalue에 대해 하나의 버전 (에 대한 lvalue 참조 허용 const)과 rvalue에 대해 하나의 버전 (rvalue 참조 허용)을 제공 할 수 있습니다.
// Overload for lvalues
void foo(my_class const& obj) // No copy, no move (just reference binding)
{
my_class copyOfObj = obj; // Copy!
// Working on copyOfObj...
}
// Overload for rvalues
void foo(my_class&& obj) // No copy, no move (just reference binding)
{
my_class copyOfObj = std::move(obj); // Move!
// Notice, that invoking std::move() is
// necessary here, because obj is an
// *lvalue*, even though its type is
// "rvalue reference to my_class".
// Working on copyOfObj...
}
위의 함수는 실제로 매우 유사하여 하나의 함수를 만들 수 있습니다. foo()함수 템플릿 이 될 수 있으며 전달 되는 개체의 이동 또는 복사본이 내부적으로 생성되는지 여부를 결정 하는 데 완벽한 전달 을 사용할 수 있습니다 .
template<typename C>
void foo(C&& obj) // No copy, no move (just reference binding)
// ^^^
// Beware, this is not always an rvalue reference! This will "magically"
// resolve into my_class& if an lvalue is passed, and my_class&& if an
// rvalue is passed
{
my_class copyOfObj = std::forward<C>(obj); // Copy if lvalue, move if rvalue
// Working on copyOfObj...
}
Scott Meyers의이 강연 을 시청하여이 디자인에 대해 더 많이 배우고 싶을 수 있습니다. ( 그가 사용하는 " Universal References " 라는 용어 가 비표준 이라는 사실을 기억하십시오 ).
한 가지 명심해야 std::forward할 점 은 일반적으로 rvalue 에 대한 이동 으로 끝나기 때문에 상대적으로 무해 해 보이지만 동일한 객체를 여러 번 전달하면 문제의 원인이 될 수 있습니다. 따라서 이것을 루프에 넣지 말고 함수 호출에서 동일한 인수를 여러 번 전달하지 않도록주의하십시오.
template<typename C>
void foo(C&& obj)
{
bar(std::forward<C>(obj), std::forward<C>(obj)); // Dangerous!
}
또한 합당한 이유가없는 한 일반적으로 템플릿 기반 솔루션에 의존하지 않습니다. 코드를 읽기 어렵게 만들기 때문입니다. 일반적으로 명확성과 단순성에 중점을 두어야합니다 .
위의 내용은 단순한 지침 일 뿐이지 만 대부분의 경우 좋은 디자인 결정을 내릴 수 있습니다.
게시물의 나머지 부분에 대해 :
내가 그것을 [...]로 다시 쓰면 2 개의 움직임과 사본이 없을 것입니다.
이것은 올바르지 않습니다. 우선 rvalue 참조는 lvalue에 바인딩 할 수 없으므로 CreditCard생성자 에 유형의 rvalue를 전달할 때만 컴파일됩니다 . 예를 들면 :
// Here you are passing a temporary (OK! temporaries are rvalues)
Account acc("asdasd",345, CreditCard("12345",2,2015,1001));
CreditCard cc("12345",2,2015,1001);
// Here you are passing the result of std::move (OK! that's also an rvalue)
Account acc("asdasd",345, std::move(cc));
하지만 이렇게하려고하면 작동하지 않습니다.
CreditCard cc("12345",2,2015,1001);
Account acc("asdasd",345, cc); // ERROR! cc is an lvalue
때문에 cc좌변과를 rvalue 참조 lvalues에 바인딩 할 수 없습니다. 또한 객체에 대한 참조를 바인딩 할 때 이동이 수행되지 않습니다 . 단지 참조 바인딩 일뿐입니다. 따라서 이동 은 하나 뿐입니다 .
당신은 당신이 걸릴 때 움직임의 수를 발생하는 것을 우려하는 경우 그래서이 응답의 첫번째 부분에 제공된 지침에 기초하여, CreditCard값, 두 생성자 과부하 하나 좌변 참조 복용 정의 할 수있다 const( CreditCard const&) 한 복용 rvalue 참조 ( CreditCard&&).
과부하 해결은 lvalue를 전달할 때 전자를 선택하고 (이 경우 하나의 복사가 수행됨) rvalue를 전달할 때 후자가 선택됩니다 (이 경우 하나의 이동이 수행됨).
Account(std::string number, float amount, CreditCard const& creditCard)
: number(number), amount(amount), creditCard(creditCard) // copy here
{ }
Account(std::string number, float amount, CreditCard&& creditCard)
: number(number), amount(amount), creditCard(std::move(creditCard)) // move here
{ }
의 사용법 std::forward<>은 일반적으로 완벽한 전달 을 원할 때 표시됩니다 . 이 경우 생성자는 실제로 생성자 템플릿 이며 다음과 같이 다소 보일 것입니다.
template<typename C>
Account(std::string number, float amount, C&& creditCard)
: number(number), amount(amount), creditCard(std::forward<C>(creditCard)) { }
어떤 의미에서 이것은 이전에 보여준 오버로드를 하나의 단일 함수로 결합합니다. lvalue를 전달하는 경우 C로 추론되며 CreditCard&참조 축소 규칙으로 인해이 함수가 인스턴스화됩니다.
Account(std::string number, float amount, CreditCard& creditCard) :
number(num), amount(amount), creditCard(std::forward<CreditCard&>(creditCard))
{ }
이것은 원인이됩니다 복사 건설 의를 creditCard당신이 원하는 것처럼. 반면에 rvalue가 전달 C되면로 추론되고 CreditCard대신이 함수가 인스턴스화됩니다.
Account(std::string number, float amount, CreditCard&& creditCard) :
number(num), amount(amount), creditCard(std::forward<CreditCard>(creditCard))
{ }
이것은 원인이됩니다 이동-건설 의을 creditCard(전달되는 값이를 rvalue이며, 그 수단은 우리가 그것을 이동할 수있는 권한이 있기 때문에) 당신이 원하는이다.