program tip

함수 반환 값에 std :: move를 언제 사용해야합니까?

radiobox 2020. 8. 4. 07:37
반응형

함수 반환 값에 std :: move를 언제 사용해야합니까? [복제]


이 질문에는 이미 답변이 있습니다.

이 경우

struct Foo {};
Foo meh() {
  return std::move(Foo());
}

새로 만든 Foo것이 xvalue 이기 때문에 이동이 불필요하다고 확신합니다 .

그러나 이런 경우에는 어떻습니까?

struct Foo {};
Foo meh() {
  Foo foo;
  //do something, but knowing that foo can safely be disposed of
  //but does the compiler necessarily know it?
  //we may have references/pointers to foo. how could the compiler know?
  return std::move(foo); //so here the move is needed, right?
}

이동이 필요하다고 생각합니다.


의 경우 때문에 12.8 / 32 불필요 :return std::move(foo);move

복사 조작 제거 기준이 충족되거나 충족 될 때 소스 오브젝트가 함수 매개 변수이고 복사 할 오브젝트가 lvalue로 지정되면 복사에 대한 생성자를 선택하기위한 과부하 해결은 다음과 같습니다. 첫 번째는 객체가 rvalue로 지정된 것처럼 수행됩니다.

return foo;NRVO의 경우이므로 복사 제거가 허용됩니다. foolvalue입니다. 따라서 "복사"에서 foo반환 값으로 선택된 생성자 meh는 이동 생성자가 있어야합니다.

추가 move가 생략되는 움직임을 방지하기 때문에, :하지만, 잠재적 인 효과를 가지고 return std::move(foo);있다 되지 NRVO을받을.

내가 아는 한 12.8 / 32 는 lvalue의 사본을 이동으로 대체 할 수있는 유일한 조건을 제시합니다. 컴파일러는 일반적으로 복사 후 lvalue가 사용되지 않음 (DFA 사용)을 감지하고 자체 이니셔티브를 변경하도록 허용되지 않습니다. 여기서는 둘 사이에 관찰 가능한 차이가 있다고 가정합니다. 관찰 가능한 동작이 동일하면 "있는 그대로"규칙이 적용됩니다.

따라서 제목의 질문에 대답하려면 std::move이동하려는 경우 반환 값을 사용하고 어쨌든 이동하지 마십시오. 그건:

  • 당신은 그것을 옮기기를 원하고
  • 그것은 lvalue이고
  • 복사 소거 대상이 아니며
  • 값별 함수 매개 변수의 이름이 아닙니다.

이것이 매우 어리 석고 이동이 일반적으로 저렴 하다는 것을 고려할 때 템플릿이 아닌 코드에서는 이것을 조금 단순화 할 수 있다고 말할 수 있습니다. 다음과 같은 std::move경우에 사용하십시오 .

  • 당신은 그것을 옮기기를 원하고
  • 그것은 lvalue이고
  • 당신은 그것에 대해 걱정할 수 없습니다.

단순화 된 규칙에 따라 일부 이동 제거를 희생합니다. 이와 같은 유형의 경우 std::vector이동하기에 비용이 적게 들며 아마도 눈치 채지 못할 것입니다. std::array이동하기에 비용이 많이 드는 유형 이나 이동이 저렴한 지 여부를 모르는 템플릿의 경우 걱정할 가능성이 더 큽니다.


두 경우 모두 이동이 불필요합니다. 두 번째 경우에는 std::move값으로 지역 변수를 반환하기 때문에 불필요하며 컴파일러는 더 이상 해당 지역 변수를 사용하지 않기 때문에 복사하지 않고 이동할 수 있다는 것을 이해합니다.


리턴 값에서 리턴 표현식이 로컬 lvalue의 이름 (즉,이 시점에서 xvalue)을 직접 참조하는 경우 std::move. 반환 표현 인 경우 반면에, 하지 식별자, 그렇게 예를 들어, 당신은 명시를해야 자동으로 이동되지 않습니다 std::move이 경우 :

T foo(bool which) {
   T a = ..., b = ...;
   return std::move(which? a : b);
   // alternatively: return which? std::move(a), std::move(b);
}

When returning a named local variable or a temporary expression directly, you should avoid the explicit std::move. The compiler must (and will in the future) move automatically in those cases, and adding std::move might affect other optimizations.


There are lots of answers about when it shouldn't be moved, but the question is "when should it be moved?"

Here is a contrived example of when it should be used:

std::vector<int> append(std::vector<int>&& v, int x) {
  v.push_back(x);
  return std::move(v);
}

ie, when you have a function that takes an rvalue reference, modifies it, and then returns a copy of it. Now, in practice, this design is almost always better:

std::vector<int> append(std::vector<int> v, int x) {
  v.push_back(x);
  return v;
}

which also allows you to take non-rvalue parameters.

Basically, if you have an rvalue reference within a function that you want to return by moving, you have to call std::move. If you have a local variable (be it a parameter or not), returning it implicitly moves (and this implicit move can be elided away, while an explicit move cannot). If you have a function or operation that takes local variables, and returns a reference to said local variable, you have to std::move to get move to occur (as an example, the trinary ?: operator).


A C++ compiler is free to use std::move(foo):

  • if it is known that foo is at the end of its lifetime, and
  • the implicit use of std::move won't have any effect on the semantics of the C++ code other than the semantic effects allowed by the C++ specification.

It depends on the optimization capabilities of the C++ compiler whether it is able to compute which transformations from f(foo); foo.~Foo(); to f(std::move(foo)); foo.~Foo(); are profitable in terms of performance or in terms of memory consumption, while adhering to the C++ specification rules.


Conceptually speaking, year-2017 C++ compilers, such as GCC 6.3.0, are able to optimize this code:

Foo meh() {
    Foo foo(args);
    foo.method(xyz);
    bar();
    return foo;
}

into this code:

void meh(Foo *retval) {
   new (retval) Foo(arg);
   retval->method(xyz);
   bar();
}

which avoids calling the copy-constructor and the destructor of Foo.


Year-2017 C++ compilers, such as GCC 6.3.0, are unable to optimize these codes:

Foo meh_value() {
    Foo foo(args);
    Foo retval(foo);
    return retval;
}

Foo meh_pointer() {
    Foo *foo = get_foo();
    Foo retval(*foo);
    delete foo;
    return retval;
}

into these codes:

Foo meh_value() {
    Foo foo(args);
    Foo retval(std::move(foo));
    return retval;
}

Foo meh_pointer() {
    Foo *foo = get_foo();
    Foo retval(std::move(*foo));
    delete foo;
    return retval;
}

which means that a year-2017 programmer needs to specify such optimizations explicitly.


std::move is totally unnecessary when returning from a function, and really gets into the realm of you -- the programmer -- trying to babysit things that you should leave to the compiler.

What happens when you std::move something out of a function that isn't a variable local to that function? You can say that you'll never write code like that, but what happens if you write code that's just fine, and then refactor it and absent-mindedly don't change the std::move. You'll have fun tracking that bug down.

The compiler, on the other hand, is mostly incapable of making these kinds of mistakes.

Also: Important to note that returning a local variable from a function does not necessarily create an rvalue or use move semantics.

See here.

참고URL : https://stackoverflow.com/questions/14856344/when-should-stdmove-be-used-on-a-function-return-value

반응형