일반적으로 / 항상 std :: move 대신 std :: forward를 사용할 수 있습니까?
저는 C ++ and Beyond 2012 컨퍼런스 에서 Universal References에 대한 Scott Meyers의 강연을 지켜 봤습니다 . 지금까지 모든 것이 의미가 있습니다. 그런데 제가 궁금했던 점에서 청중이 약 50 분쯤에 질문을합니다. Meyers는 대답이 비 관상적이고 그의 마음을 어리석게 할 것이기 때문에 대답에 관심이 없다고 말하지만 여전히 관심이 있습니다.
제시된 코드는 다음과 같습니다.
// Typical function bodies with overloading:
void doWork(const Widget& param) // copy
{
// ops and exprs using param
}
void doWork(Widget&& param) // move
{
// ops and exprs using std::move(param)
}
// Typical function implementations with universal reference:
template <typename T>
void doWork(T&& param) // forward => copy and move
{
// ops and exprs using std::forward<T>(param)
}
요점은 우리가 rvalue 참조를 취할 때 우리가 rvalue가 있다는 것을 알고 있으므로 std::move
그것이 rvalue라는 사실을 보존 해야한다는 것입니다. 범용 참조 ( T&&
, 여기서는 T
추론 된 유형 임) std::forward
를 사용할 때 이것이 lvalue 또는 rvalue 일 수 있다는 사실을 보존 하려고 합니다.
그래서 질문은 : std::forward
함수에 전달 된 값이 lvalue인지 rvalue인지를 보존하고 std::move
단순히 인수를 rvalue로 캐스팅하기 때문에 우리는 std::forward
어디에서나 사용할 수 있습니까? 것 std::forward
처럼 행동 std::move
우리가 사용하는 모든 경우에 std::move
, 또는 마이어스 '일반화에 의해 놓친 행동의 몇 가지 중요한 차이가?
나는 Meyers가 올바르게 말했듯이 완전히 비관 상적이지만 다음도 유효한 사용이기 때문에 누구든지해야한다고 제안하지 않습니다 std::move
.
void doWork(Widget&& param) // move
{
// ops and exprs using std::forward<Widget>(param)
}
이 두 가지는 매우 다르고 보완적인 도구입니다.
std::move
인수를 추론 하고 무조건 rvalue 표현식을 생성합니다. 이것은 실제 개체 또는 변수에 적용하는 것이 좋습니다.std::forward
필수 템플릿 인수를 취하고 (이것을 지정해야합니다!) 유형이 무엇인지에 따라 마술처럼 lvalue 또는 rvalue 표현식을 생성합니다 (추가&&
및 축소 규칙을 통해). 이것은 추론 된 템플릿 함수 인수에만 적용 할 수 있습니다.
아마도 다음 예제가이를 좀 더 잘 보여줍니다.
#include <utility>
#include <memory>
#include <vector>
#include "foo.hpp"
std::vector<std::unique_ptr<Foo>> v;
template <typename T, typename ...Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); // #1
}
int main()
{
{
std::unique_ptr<Foo> p(new Foo('a', true, Bar(1,2,3)));
v.push_back(std::move(p)); // #2
}
{
v.push_back(make_unique<Foo>('b', false, Bar(5,6,7))); // #3
}
{
Bar b(4,5,6);
char c = 'x';
v.push_back(make_unique<Foo>(c, b.ready(), b)); // #4
}
}
상황 # 2에서는 기존의 구체적인 객체 p
가 있으며 무조건 이동하려고합니다. 만 std::move
의미가 있습니다. 여기에 "전달"할 항목이 없습니다. 이름이 지정된 변수가 있으며 여기서 이동하려고합니다.
On the other hand, situation #1 accepts a list of any sort of arguments, and each argument needs to be forwarded as the same value category as it was in the original call. For example, in #3 the arguments are temporary expressions, and thus they will be forwarded as rvalues. But we could also have mixed in named objects in the constructor call, as in situation #4, and then we need forwarding as lvalues.
Yes, if param
is a Widget&&
, then the following three expressions are equivalent (assuming that Widget
is not a reference type):
std::move(param)
std::forward<Widget>(param)
static_cast<Widget&&>(param)
In general (when Widget
may be a reference), std::move(param)
is equivalent to both of the following expressions:
std::forward<std::remove_reference<Widget>::type>(param)
static_cast<std::remove_reference<Widget>::type&&>(param)
Note how much nicer std::move
is for moving stuff. The point of std::forward
is that it mixes well with template type deduction rules:
template<typename T>
void foo(T&& t) {
std::forward<T>(t);
std::move(t);
}
int main() {
int a{};
int const b{};
//Deduced T Signature Result of `forward<T>` Result of `move`
foo(a); //int& foo(int&) lvalue int xvalue int
foo(b); //int const& foo(int const&) lvalue int const xvalue int const
foo(int{});//int foo(int&&) xvalue int xvalue int
}
'IT박스' 카테고리의 다른 글
RabbitMQ : 토픽 교환이있는 지속성 메시지 (0) | 2020.11.25 |
---|---|
Service.onStartCommand에서 stopSelf ()를 호출 할 수 있습니까? (0) | 2020.11.25 |
Gather (tidyr)와 용융 (reshape2) 비교 (0) | 2020.11.25 |
쿠키 경로 및 하위 폴더 페이지에 대한 액세스 가능성 (0) | 2020.11.25 |
컨트롤러에서 HtmlHelper 사용 (0) | 2020.11.25 |