이동 의미론은 값을 반환 할 때 반드시 크게 개선되는 것은 아닙니다. 그리고 shared_ptr
(또는 비슷한 것을 사용하는 경우 ) 아마도 비관적 일 것입니다. 실제로 거의 모든 합리적으로 현대적인 컴파일러는 RVO (Return Value Optimization) 및 NRVO (Named Return Value Optimization)라는 기능을 수행합니다. 즉, 실제로 값 을 복사하는 대신 값을 반환 할 때, 그들은 단순히 반환 후 값이 할당 될 위치에 숨겨진 포인터 / 참조를 전달하고 함수는 그것을 사용하여 값이 끝나는 값을 만듭니다. C ++ 표준에는이를 허용하기위한 특수한 조항이 포함되어 있으므로 (예를 들어) 복사 생성자가 부작용을 보이는 경우에도 복사 생성자를 사용하여 값을 반환 할 필요는 없습니다. 예를 들면 다음과 같습니다.
#include <vector>
#include <numeric>
#include <iostream>
#include <stdlib.h>
#include <algorithm>
#include <iterator>
class X {
std::vector<int> a;
public:
X() {
std::generate_n(std::back_inserter(a), 32767, ::rand);
}
X(X const &x) {
a = x.a;
std::cout << "Copy ctor invoked\n";
}
int sum() { return std::accumulate(a.begin(), a.end(), 0); }
};
X func() {
return X();
}
int main() {
X x = func();
std::cout << "sum = " << x.sum();
return 0;
};
여기서 기본 아이디어는 매우 간단합니다. 가능하면 복사하지 않는 충분한 내용의 클래스를 만듭니다 ( std::vector
32767의 임의 정수로 채 웁니다). 우리는 그것이 언제 / 복사되는지 보여줄 명백한 사본 ctor를 가지고 있습니다. 또한 객체의 임의의 값으로 무언가를 수행하는 코드가 조금 더 있으므로 최적화 프로그램은 아무것도하지 않기 때문에 클래스에 대한 모든 것을 제거하지 않습니다.
그런 다음 함수에서 이러한 객체 중 하나를 반환하는 코드가 있고 합산을 사용하여 객체가 완전히 무시되는 것이 아니라 실제로 만들어 졌는지 확인합니다. 적어도 최신 / 현대 컴파일러로 실행할 때 우리가 작성한 복사 생성자가 전혀 실행되지 않는다는 것을 알았습니다. 그렇습니다. 빠른 복사조차도 shared_ptr
복사하지 않는 것보다 느립니다. 조금도.
이사를하면 단순히 없이는 직접 할 수없는 많은 일을 할 수 있습니다. 외부 병합 정렬의 "병합"부분을 고려하십시오. 예를 들어 8 개의 파일을 병합 할 수 있습니다. 이상적으로 당신이로 그 파일을 모두 팔을 넣어 싶습니다 vector
- 이후 있지만 vector
(현재의 C ++ 03) 요소를 복사 할 수 있어야하고, ifstream
s는 일부 나왔습니다 붙어, 복사 할 수 없습니다 unique_ptr
/ shared_ptr
, 벡터에 넣을 수 있도록 참고 심지어 우리의 (예를 들어)의 경우 reserve
공간 vector
우리가 반드시 우리의 것, 그래서 ifstream
코드가 비록 컴파일되지 않도록, s는 정말 복사되지 않습니다 컴파일러는 것을 알 수 없습니다 우리는 복사 생성자가 없을 것 알고 어쨌든 사용.
여전히 복사 할 수는 없지만 C ++ 11에서는 ifstream
을 이동할 수 있습니다. 이 경우 객체 는 움직일 수 없지만 필요한 경우 컴파일러를 행복하게 유지하여 스마트 포인터 해킹없이 ifstream
객체를 vector
직접 넣을 수 있습니다 .
벡터 수행 확장은 이동의 의미가 정말 할 수있는 시간이 꽤 괜찮은 예이다 / 유용하지만입니다. 이 경우 RVO / NRVO는 도움이되지 않을 것입니다. 함수의 리턴 값을 다루지 않기 때문입니다. 우리는 하나의 객체를 가지고있는 하나의 벡터를 가지고 있으며, 그 객체를 새로운 큰 메모리 덩어리로 옮기고 싶습니다.
C ++ 03에서는 새 메모리에 객체의 사본을 만든 다음 이전 메모리의 기존 객체를 삭제하여 수행했습니다. 그러나 모든 사본을 기존 사본을 버리는 것만으로도 많은 시간을 낭비했습니다. C ++ 11에서는 대신 이동할 수 있습니다. 이를 통해 일반적으로 (일반적으로 훨씬 느린) 딥 카피 대신 얕은 카피를 수행 할 수 있습니다. 다시 말해, 문자열이나 벡터 (두 개의 예제에만 해당)를 사용하면 포인터가 참조하는 모든 데이터를 복사하는 대신 객체에 포인터를 복사하면됩니다.
shared_ptr
빠른 복사를 위해서만 주의해야합니다 ) 이동 시맨틱은 코딩, 시맨틱 및 청결-벌금이 거의없이 동일하게 달성 될 수 있습니다.