IT박스

일반적으로 / 항상 std :: move 대신 std :: forward를 사용할 수 있습니까?

itboxs 2020. 11. 25. 07:47
반응형

일반적으로 / 항상 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
}

참고URL : https://stackoverflow.com/questions/13219484/can-i-typically-always-use-stdforward-instead-of-stdmove

반응형