복사보다 이동이 상대적으로 저렴하다는 것은 이제 알고 있다.

이동에 대해 좀 더 근거있는 기대를 가지게 하기 위해 몇 가지 사항을 짚고 갈 필요가 있다.

 

C++11에 들어서며 C++98을 개선하여 이동이 복사보다 연산이 빠른 타입에 대해서는 이동 연산을 제공해주게 되었다.

그런데 사용자의 프로그램이 C++11에 맞춰서 완벽히 개정되지 않았다면 그러한 혜택을 받지 못할 수도 있다.

 

 

이동을 명시적으로 지원하는 타입에서도 의외로 성능상 이점이 크지 않을 수도 있다.

예를 들어 C++11 STL의 모든 컨테이너는 이동을 지원하지만 모든 컨테이너의 이동이 저렴하다고 가정해서는 안된다.

 

std::array를 제외한 다른 컨테이너들은 내용을 힙에 저장하고 그 힙 메모리를 가리키는 포인터만 담고 있기 때문에 컨테이너 전체 내용을 상수 시간으로 이동할 수 있을 뿐만 아니라 원본 컨테이너의 힙 메모리를 가리키는 포인터만 대상 컨테이너로 복사하고 원본 컨테이너의 포인터를 널로 설정하기만 하면 컨테이너 내용 전체가 옮겨진것과 같은 결과가 된다.

 

그에 비해 std::array는 내장 배열에 STL 인터페이스를 씌운 것에 불과하기 때문에 내용이 std::array 객체 자체에 직접 저장된다. std::array에 저장된 타입의 이동 연산이 복사 연산보다 빠르다면 std::array를 복사하는 것보다 빠른것은 사실이지만 std::array는 이동이나 복사나 둘다 계산 복잡도가 선형이다. 포인터를 복사하는 것만큼 저렴하지는 않은 것이다.

 

 

string은 상수 시간 이동과 선형 시간 복사를 제공한다. 당연히 상수 시간에 이루어지는 이동이 항상 빠를 것 같지만 아주 작은 문자열의 경우 작은 문자열 최적화(SSO)를 사용하는 경우가 많기 때문에 '항상' 성립하지는 않는다.

 

 

항목 14에서 다뤘듯이 이동 연산이 예외를 던지지 않는다는 것이 확실한 경우에만 복사 연산을 이동 연산으로 대체한다. 이동 연산이 noexcept로 선언되어있지 않으면 컴파일러는 계속 복사 연산을 호출할 수 있다.

 

C++11의 이동 의미론이 도움되지 않는 시나리오

◾ 이동 연산이 없다 : 복사 요청으로 대체된다.

◾ 이동이 더 빠르지 않다 : 복사 연산보다 빠르지 않다.

◾ 이동을 사용할 수 없다 : 이동 연산이 noexcept로 선언되어 있지 않다.

◾ 원본 객체가 왼값이다 : 아주 드문 경우(항목 25)이지만 오른값만 이동 연산의 원본이 될 수 있는 경우도 있다.

 

 

핵심은 항목의 이름처럼 이동 연산들이 존재하지 않고, 저렴하지 않고, 적용되지 않는다고 가정하라는 것이다.

템플릿을 작성할 때에는 코드에 쓰이는 모든 타입을 알 수 없기 때문에 대체적으로 그런 가정이 사실이다.

그런 경우에는 이동 의미론이 존재하기 전처럼 객체의 복사를 보수적으로 다루어야 한다.

 

◾ 이동 연산들이 존재하지 않고, 저렴하지 않고, 적용되지 않을 것이라고 가정하라.

◾ 타입들과 이동 의미론 지원 여부를 미리 알 수 있는 경우에는 그런 가정을 둘 필요가 없다.

+ Recent posts