오른쪽 값 시리즈 의 5:이상 안전 한 이동
C++의 효율 적 인 값 유형 에 관 한 시리즈 의 다섯 번 째 편 에 오신 것 을 환영 합 니 다.지난 편 에서 우 리 는 이전 할당 의 최 적 화 를 위 한 끊 임 없 는 검색 에 머 물 렀 다.오늘 우 리 는 이'도시 이전(Move City)'을 가로 지 르 는 길 을 찾 을 것 이다.여기 서 가장 일반적인 유형 은 놀 라 운 충돌 이 있 을 수 있다.
앞의 글 에서 우 리 는'이동 할 수 있 는 권한'을 제공 함으로써 같은 코드 를 이동 할 수 있 는 유형 과 이동 할 수 없 는 유형 에 동시에 사용 하고 가능 할 때 이동 최 적 화 를 이용 할 수 있 음 을 보 았 다.이러한'가능 할 때 이동 하고 필요 할 때 복사'방법 은 코드 최적화 에 매우 유용 하 며 구조 함 수 를 옮 기지 않 은 낡은 유형 에 도 호 환 된다.그러나 강 한 이상 보증 을 제공 하 는 조작 에 있어 서 는 새로운 부담 이 증가 했다.
강 이상 보증,강 이상 요구
강 한 이상 보증 을 실현 하려 면 특정한 작업 의 모든 절 차 를 두 가지 로 나 누 어야 한다.
만약 우리 가 모든 동작 을 이 두 가지 로 나 누고 그 어떠한 첫 번 째 동작 도 두 번 째 동작 전에 발생 하도록 보장 한다 면 우 리 는 아무 일 도 없 을 것 이다.C++03 에서 하나의 전형 적 인 예 가 있 습 니 다.vector:reserve()가 새로운 요소 에 메모 리 를 할당 해 야 할 때:
void reserve(size_type n)
{
if (n > this->capacity())
{
pointer new_begin = this->allocate( n );
size_type s = this->size(), i = 0;
try
{
// copy to new storage: can throw; doesn't modify *this
for (;i < s; ++i)
new ((void*)(new_begin + i)) value_type( (*this)[i] );
}
catch(...)
{
while (i > 0) // clean up new elements
(new_begin + --i)->~value_type();
this->deallocate( new_begin ); // release storage
throw;
}
// -------- irreversible mutation starts here -----------
this->deallocate( this->begin_ );
this->begin_ = new_begin;
this->end_ = new_begin + s;
this->cap_ = new_begin + n;
}
}
이전 작업 을 지원 하 는 실현 이 라면 try 블록 에 std:move 의 명시 적 호출 을 추가 하고 순환 을 다음 으로 바 꿔 야 합 니 다.
for (;i < s; ++i)
new ((void*)(new_begin + i)) value_type( std::move( (*this)[i] ) );
이 변화 에서 재 미 있 는 것 은 valuetype 은 이전 을 지원 합 니 다.그러면 순환 에서*this(왼쪽 값 에서 명시 적 이전 요청 을 하 는 것 은 논리 적 으로 바 꾸 는 작업 입 니 다)를 바 꿉 니 다.
현재 이전 작업 이 이상 을 던 지면 이 순환 은 되 돌 릴 수 없 는 변화 가 생 길 것 이다.한 부분 을 되 돌려 야 하 는 순환 은 더 많은 이전 작업 이 필요 하기 때문이다.그래서 valuetype 은 이전 을 지원 하 는 상황 에서 강 한 이상 보증 을 유지 합 니 다.이전 구조 함 수 는 반드시 던 지지 않 아야 합 니 다.
던 지 는 전이 작업 이 있 을 수 있 습 니 다.잠재 적 으로 다시 던 지 는 스크롤 백 을 할 수 없습니다.
결실
C++0x 표준 초안 에 서 는 기본적으로 던 질 수 있 는 전이 구조 함 수 를 반대 합 니 다.우 리 는 이 규칙 을 준수 하 는 것 을 권장 합 니 다.그러나 구조 함 수 를 옮 기 려 면 이 규칙 을 던 지지 않 아야 한다.항상 쉽게 지 키 는 것 은 아니다.std::pair<:string>을 예 로 들 면 User Type 은 복제 구조 함 수 를 던 질 수 있 는 형식 입 니 다.C++03 에서 이 유형 은 문제 가 없습니다.std::vector 에 사용 할 수 있 습 니 다.그러나 C++0x 에서 std:string 은 전이 구조 함 수 를 가지 고 있 습 니 다.std:pair 역시 있 습 니 다.
template
pair::pair(pair&& x)
: first(std::move(x.first))
, second(std::move(x.second))
{}
여기 가 문제 야.second 의 유형 은 User Type 입 니 다.구조 함 수 를 옮 기지 않 았 습 니 다.이것 은 second 의 구 조 는 이전 구조 가 아니 라 복사 구조 임 을 의미 합 니 다.그래서 pair<:string usertype=">는 던 질 수 있 는 전이 구조 함 수 를 보 여 줍 니 다.std::vector 에서 강 한 이상 보증 을 파괴 하지 않 고 사용 할 수 없습니다.
오늘 은 pair 를 사용 할 수 있 도록 다음 코드 와 유사 한 것 이 필요 하 다 는 것 을 의미 합 니 다.
template
pair(pair&& rhs
, typename enable_if< // Undocumented optional
mpl::and_< // argument, not part of the
boost::has_nothrow_move // public interface of pair.
, boost::has_nothrow_move
>
>::type* = 0
)
: first(std::move(rhs.first)),
second(std::move(rhs.second))
{};
enable 사용 하기if,이 구조 함 수 를"사라 지게"할 수 있 습 니 다.has 를 제외 하고.nothrow_move 는 T1 과 T2 모두 true 입 니 다.
우 리 는 전이 구조 함수 가 존재 하 는 지 확인 할 방법 이 없다 는 것 을 알 고 있 습 니 다.던 지지 않 았 는 지 는 말 할 것 도 없습니다.따라서 우리 가 새로운 언어 특성 을 얻 기 전에 boost::hasnothrow_move 는 사용자 정의 형식 을 false 로 되 돌려 주 는 방법 중 하나 입 니 다.따라서 이전 구조 함 수 를 만 들 때 이 trait 를 특 화 해 야 합 니 다.예 를 들 어 만약 에 우리 가 std::vector 와 std::pair 에 전이 구조 함 수 를 추가 했다 면 우 리 는 다음 과 같이 추가 해 야 합 니 다.
namespace boost
{
// All vectors have a (nothrow) move constructor
template
struct has_nothrow_move<:vector> > : true_type {};
// A pair has a (nothrow) move constructor iff both its
// members do as well.
template
struct has_nothrow_move<:pair> >
: mpl::and_<
boost::has_nothrow_move
, boost::has_nothrow_move
> {};
}
우 리 는 이것 이 매우 보기 싫다 는 것 을 인정한다.C++위원 회 는 아직도 이 문 제 를 어떻게 해결 할 것 인 가 를 토론 하고 있 지만 다음 과 같은 일 들 은 모두 일반적인 동 의 를 얻 었 다.
4.567917.우 리 는 강 한 이상 보증 을 조용히 포기 했다 고 해서 기 존의 코드 를 파괴 해 서 는 안 된다
4.567917.적당 한 시기 에 부족 한 전이 구조 함 수 를 생 성 할 수 있 습 니 다.Bjarne Stroustrup 이 N2904 에서 제안 한 것 처럼 이 문 제 를 줄 일 수 있 습 니 다.이것 은 pair 와 모든 유사 한 유형의 문 제 를 복구 할 수 있 고 생 성 된 이동 최 적 화 를 증가 함으로써 일부 코드 의 속 도 를 무료 로 향상 시 킬 수 있다
4.567917.아니면 우리 가'수공'으로 처리 해 야 하 는 유형 이 있 습 니 다
"문제 있 는 타 입".
문제 가 있 는 유형 으로 돌아 가면 우리 가 옮 기 고 싶 은 하위 대상 을 가지 고 있 습 니 다.안전 한 실현 을 제 공 했 습 니 다.그리고 우리 가 필요 로 하 는 다른 하위 대상 도 있 습 니 다.std::vector 는 하나의 예 입 니 다.분배 기 를 가지 고 있 습 니 다.복사 구조 함수 가 이상 을 던 질 수 있 습 니 다.
vector(vector&& rhs)
: _alloc( std::move(rhs._alloc) )
, _begin( rhs._begin )
, _end( rhs._end )
, _cap( rhs._cap )
{
// "something else"
rhs._begin = rhs._end = rhs._cap = 0;
}
N2904 에서 말 한 결 성 생 성 된 것 과 같은 간단 한 구성원 식 이전 은 여기 서 정확 한 의 미 를 가지 지 못 할 것 이다.특히 rhs 의begin, _end 및cap 제로.하지만 만약alloc 는 던 지지 않 은 전이 구조 함수 가 없 으 면 두 번 째 줄 에서 만 복사 할 수 있 습 니 다.이 복사 가 이상 을 던 질 수 있다 면,vector 는 던 질 수 있 는 전이 구조 함 수 를 제공 합 니 다.
언어 디자이너 에 게 도전 은 사용자 에 게 똑 같은 정 보 를 두 번 제공 하 라 고 요구 하 는 것 을 어떻게 피 하 는 것 입 니까?구조 함 수 를 옮 기 는 서명 에서 구성원 의 유형 을 옮 길 수 있 는(앞의 pair 전이 구조 함수 중의 5,6 줄)이 라 고 가리 키 는 동시에 구성원 초기 화 목록 에서 구성원 을 진정 으로 옮 겨 야 한다(10,11 줄).현재 토론 중인 가능성 은 새로운 속성 문법 을 사용 하여 vector 로 이동 하 는 구조 함 수 를 이렇게 쓸 수 있 도록 하 는 것 입 니 다.
vector(vector&& rhs) [[moves(_alloc)]]
: _begin( rhs._begin )
, _end( rhs._end )
, _cap( rhs._cap )
{
rhs._begin = rhs._end = rhs._cap = 0;
}
이 구 조 는 를 제외 하고 SFINAE 에 의 해 떨 어 질 것 이다.alloc 자 체 는 던 지지 않 은 전이 구조 함 수 를 가지 고 있 으 며,이 구성원 은 rhs 의 해당 구성원 에서 옮 겨 와 암시 적 으로 초기 화 됩 니 다.
불 행 히 도 C++0x 의 속성 에 대한 역할 에 대해 약간의 차이 가 존재 하기 때문에 우 리 는 위원회 가 어떤 문법 을 받 아들 일 지 모 르 지만 적어도 우 리 는 원칙적으로 문제 가 어디 에 있 는 지,그리고 그것 을 어떻게 해결 하 는 지 이해 했다 고 생각한다.
후속
자,읽 어 주 셔 서 감사합니다.오늘 은 여기까지.다음 편 에 서 는 완벽 한 퍼 가기 에 대해 토론 할 것 입 니 다.그리고 우 리 는 당신 에 게 C+03 의 이전 시 뮬 레이 션 에 관 한 조 사 를 빚 진 것 도 잊 지 않 았 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Vector & Matrix스칼라 : 하나의 숫자로만 이루어진 데이터 (크기만 있고 방향이 없는 물리량) 벡터 : 여러 숫자로 이루어진 데이터 레코드. 매트릭스 : 벡터가 여럿인 데이터집합 벡터의 크기는 스칼라배를 통해 표현할 수 있다. *내...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.