Program Tip

std :: move () 란 무엇이며 언제 사용해야합니까?

programtip 2020. 10. 3. 11:32
반응형

std :: move () 란 무엇이며 언제 사용해야합니까?


  1. 뭔데?
  2. 그것은 무엇을합니까?
  3. 언제 사용해야합니까?

좋은 링크 감사합니다.


C ++ 11 R- 값 참조 및 이동 생성자에 대한 Wikipedia 페이지

  1. C ++ 11에서는 복사 생성자 외에도 객체에 이동 생성자가있을 수 있습니다.
    (그리고 복사 할당 연산자 외에도 이동 할당 연산자가 있습니다.)
  2. 객체에 "rvalue-reference"( Type &&) 유형이있는 경우 복사 생성자 대신 이동 생성자가 사용됩니다 .
  3. std::move() 객체에서 이동할 수 있도록 객체에 대한 rvalue 참조를 생성하는 캐스트입니다.

복사를 피하는 새로운 C ++ 방법입니다. 예를 들어 이동 생성자를 사용하여 a는 std::vector데이터에 대한 내부 포인터를 새 개체에 복사하여 이동 된 개체를 잘못된 상태로 남겨 모든 데이터를 복사하지 않도록 할 수 있습니다. 이것은 C ++에서 유효합니다.

이동 의미론, rvalue, 완벽한 전달을 위해 인터넷 검색을 시도하십시오.


1. "무엇입니까?"

std::move()기술적으로는 함수 이지만 실제로 는 함수 가 아니라고 말할 있습니다 . 컴파일러가 식의 값을 고려하는 방식 사이 의 일종의 변환기 입니다.

2. "무엇을합니까?"

가장 먼저 주목해야 할 것은 std::move() 실제로 아무것도 움직이지 않는다는 것 입니다. 표현식을 lvalue (예 : 명명 된 변수)에서 xvalue로 변환 합니다. xvalue는 컴파일러에 다음과 같이 알려줍니다.

당신은 나를 약탈하고, 내가 들고있는 것을 옮기고 다른 곳에서 사용할 수 있습니다 (어쨌든 곧 파괴 될 것이기 때문에). "

즉,을 사용 std::move(x)하면 컴파일러가 x. 따라서 x메모리에 자체 버퍼가있는 std::move()경우 컴파일러는 대신 다른 객체를 소유 할 수 있습니다.

prvalue 에서 이동할 수도 있지만 (예 : 일시적으로 전달), 거의 유용하지 않습니다.

3. "언제 사용해야합니까?"

이 질문을하는 또 다른 방법은 "기존 개체의 리소스를 무엇을 위해 잠식 할 것인가?"입니다. 글쎄, 응용 프로그램 코드를 작성하는 경우 컴파일러가 만든 임시 개체를 많이 사용하지 않을 것입니다. 그래서 주로 생성자, 연산자 메서드, 표준 라이브러리 알고리즘과 같은 함수 등과 같은 곳에서이 작업을 수행합니다. 여기서 객체는 자동으로 많이 생성되고 파괴됩니다. 물론 그것은 단지 경험의 법칙입니다.

일반적인 용도는 복사하는 대신 한 개체에서 다른 개체로 리소스를 '이동'하는 것입니다. @Guillaume 은 간단한 예 를 들어이 페이지로 연결됩니다 : 적은 복사로 두 개체를 교체하는 것입니다.

template <class T>
swap(T& a, T& b) {
    T tmp(a);   // we now have two copies of a
    a = b;      // we now have two copies of b (+ discarded a copy of a)
    b = tmp;    // we now have two copies of tmp (+ discarded a copy of b)
}

move를 사용하면 리소스를 복사하는 대신 교체 할 수 있습니다.

template <class T>
swap(T& a, T& b) {
    T tmp(std::move(a));
    a = std::move(b);   
    b = std::move(tmp);
}

때 발생하는 생각 T, 말, 인 vector<int>크기 n의. 첫 번째 버전에서는 3 * n 요소를 읽고 쓰고, 두 번째 버전에서는 기본적으로 벡터의 버퍼에 대한 포인터 3 개와 버퍼 크기 3 개만 읽고 씁니다. 물론 수업 T은 이사하는 방법을 알아야합니다. T이 작업을 수행하려면 클래스 이동 할당 연산자와 이동 생성자가 있어야합니다.


복사를하지 않고 객체의 내용을 다른 곳으로 "전송"해야 할 때 move를 사용할 수 있습니다. std :: move를 사용하여 객체가 복사를 수행하지 않고 임시 객체의 내용을 가져 오는 것도 가능합니다 (많은 시간 절약).

이 링크는 정말 도움이되었습니다.

http://thbecker.net/articles/rvalue_references/section_01.html

I'm sorry if my answer is coming too late, but I was also looking for a good link for the std::move, and I found the links above a little bit "austere".

This puts the emphasis on r-value reference, in which context you should use them, and I think it's more detailed, that's why I wanted to share this link here.


Q: What is std::move?

A: std::move() is a function from the C++ Standard Library for casting to a rvalue reference.

Simplisticly std::move(t) is equivalent to:

static_cast<T&&>(t);

An rvalue is a temporary that does not persist beyond the expression that defines it, such as a intermediate function result which is never stored in a variable.

int a = 3; // 3 is a rvalue, does not exist after expression is evaluated
int b = a; // a is a lvalue, keeps existing after expression is evaluated

An implementation for std::move() is given in N2027: "A Brief Introduction to Rvalue References" as follows:

template <class T>
typename remove_reference<T>::type&&
std::move(T&& a)
{
    return a;
}

As you can see, std::move returns T&& no matter if called with a value (T), reference type (T&) or rvalue reference (T&&).

Q: What does it do?

A: As a cast, it does not do anything during runtime. It is only relevant at compile time to tell the compiler that you would like to continue considering the reference as an rvalue.

foo(3 * 5); // obviously, you are calling foo with a temporary (rvalue)

int a = 3 * 5;
foo(a);     // how to tell the compiler to treat `a` as an rvalue?
foo(std::move(a)); // will call `foo(int&& a)` rather than `foo(int a)` or `foo(int& a)`

What it does not do:

  • Make a copy of the argument
  • Call the copy constructor
  • Change the argument object

Q: When should it be used?

A: You should use std::move if you want to call functions which support move semantics with an argument which is not an rvalue (temporary expression).

This begs the following follow-up questions for me:

  • What are move semantics? Move semantics in contrast to copy semantics is a programming technique in which the members of a object are initialized by 'taking over' instead of copying another object's members. Such 'take over' makes only sense with pointers and resource handles, which can be cheaply transferred by copying the pointer or integer handle rather than the underlying data.

  • What kind of classes and objects support move semantics? It is up to you as a developer to implement move semantics in your own classes if these would benefit from transferring their members instead of copying them. Once you implement move semantics, you will directly benefit from work from many library programmers who have added support for handling classes with move semantics efficiently.

  • Why can't the compiler figure it out on its own? The compiler cannot just call another overload of a function unless you say so. You must help the compiler choose whether the regular or move version of function should be called.

  • In which situations would I want to tell the compiler that it should treat a variable as an rvalue? This will most likely happen in template or library functions, where you know that an intermediate result could be salvaged.


std::move itself doesn't really do much. I thought that it called the moved constructor for an object, but it really just performs a type cast (casting an lvalue variable to an rvalue so that the said variable can be passed as an argument to a move constructor or assignment operator).

So std::move is just used as a precursor to using move semantics. Move semantics is essentially an efficient way for dealing with temporary objects.

Consider Object A = B + C + D + E + F;

This is nice looking code, but E + F produces a temporary object. Then D + temp produces another temporary object and so on. In each normal "+" operator of a class, deep copies occur.

For example

Object Object::operator+ (const Object& rhs) {
    Object temp (*this);
    // logic for adding
    return temp;
}

The creation of the temporary object in this function is useless - these temporary objects will be deleted at the end of the line anyway as they go out of scope.

We can rather use move semantics to "plunder" the temporary objects and do something like

 Object& Object::operator+ (Object&& rhs) {
     // logic to modify rhs directly
     return rhs;
 }

This avoid needless deep copies being made. With reference to the example, the only part where deep copying occurs is now E + F. The rest uses move semantics. The move constructor or assignment operator also needs to be implemented to assign the result to A.


"What is it?" and "What does it do?" has been explained above.

I will give a example of "when it should be used".

For example, we have a class with lots of resource like big array in it.

class ResHeavy{ //  ResHeavy means heavy resource
    public:
        ResHeavy(int len=10):_upInt(new int[len]),_len(len){
            cout<<"default ctor"<<endl;
        }

        ResHeavy(const ResHeavy& rhs):_upInt(new int[rhs._len]),_len(rhs._len){
            cout<<"copy ctor"<<endl;
        }

        ResHeavy& operator=(const ResHeavy& rhs){
            _upInt.reset(new int[rhs._len]);
            _len = rhs._len;
            cout<<"operator= ctor"<<endl;
        }

        ResHeavy(ResHeavy&& rhs){
            _upInt = std::move(rhs._upInt);
            _len = rhs._len;
            rhs._len = 0;
            cout<<"move ctor"<<endl;
        }

    // check array valid
    bool is_up_valid(){
        return _upInt != nullptr;
    }

    private:
        std::unique_ptr<int[]> _upInt; // heavy array resource
        int _len; // length of int array
};

Test code:

void test_std_move2(){
    ResHeavy rh; // only one int[]
    // operator rh

    // after some operator of rh, it becomes no-use
    // transform it to other object
    ResHeavy rh2 = std::move(rh); // rh becomes invalid

    // show rh, rh2 it valid
    if(rh.is_up_valid())
        cout<<"rh valid"<<endl;
    else
        cout<<"rh invalid"<<endl;

    if(rh2.is_up_valid())
        cout<<"rh2 valid"<<endl;
    else
        cout<<"rh2 invalid"<<endl;

    // new ResHeavy object, created by copy ctor
    ResHeavy rh3(rh2);  // two copy of int[]

    if(rh3.is_up_valid())
        cout<<"rh3 valid"<<endl;
    else
        cout<<"rh3 invalid"<<endl;
}

output as below:

default ctor
move ctor
rh invalid
rh2 valid
copy ctor
rh3 valid

We can see that std::move with move constructor makes transform resource easily.

Where else is std::move useful?

std::move can also be useful when sorting an array of elements. Many sorting algorithms (such as selection sort and bubble sort) work by swapping pairs of elements. Previously, we’ve had to resort to copy-semantics to do the swapping. Now we can use move semantics, which is more efficient.

It can also be useful if we want to move the contents managed by one smart pointer to another.

Cited:

참고URL : https://stackoverflow.com/questions/3413470/what-is-stdmove-and-when-should-it-be-used

반응형