2016 Oulu ISO C ++ 표준 회의에서 단순화 된 값 범주를 통한 복사 제거 보장 이라는 제안 이 표준위원회에 의해 C ++ 17로 투표되었습니다.
보장 된 복사 제거는 정확히 어떻게 작동합니까? 복사 제거가 이미 허용 된 일부 경우를 포함합니까, 아니면 복사 제거를 보장하기 위해 코드를 변경해야합니까?
2016 Oulu ISO C ++ 표준 회의에서 단순화 된 값 범주를 통한 복사 제거 보장 이라는 제안 이 표준위원회에 의해 C ++ 17로 투표되었습니다.
보장 된 복사 제거는 정확히 어떻게 작동합니까? 복사 제거가 이미 허용 된 일부 경우를 포함합니까, 아니면 복사 제거를 보장하기 위해 코드를 변경해야합니까?
답변:
여러 상황에서 복사 제거가 허용되었습니다. 그러나 허용 되더라도 코드는 사본이 제거되지 않은 것처럼 작동 할 수 있어야했습니다. 즉, 액세스 가능한 복사 및 / 또는 이동 생성자가 있어야합니다.
보장 된 복사 제거는 복사 / 이동이 제거 될 수있는 특정 상황이 실제로 복사 / 이동 을 전혀 유발하지 않도록 여러 C ++ 개념을 재정의 합니다 . 컴파일러는 복사본을 제거하지 않습니다. 표준은 그러한 복사가 결코 일어날 수 없다고 말합니다.
이 기능을 고려하십시오.
T Func() {return T();}
비 보장 복사 제거 규칙에 따라 임시를 만든 다음 해당 임시에서 함수의 반환 값으로 이동합니다. 그 이동 작업 은 생략 될 수 있지만T
사용되지 않는 경우에도 액세스 가능한 이동 생성자가 있어야합니다.
비슷하게:
T t = Func();
의 복사 초기화입니다 t
. 이것은 t
의 반환 값으로 초기화 를 복사 Func
합니다. 그러나 T
호출되지 않더라도 이동 생성자가 있어야합니다.
보장 된 복사 제거 는 prvalue 표현식의 의미를 재정의합니다 . C ++ 17 이전, prvalue는 임시 개체입니다. C ++ 17에서 prvalue 표현식은 일시적인 것을 구체화 할 수있는 것일 뿐이지 만 아직 임시적인 것은 아닙니다.
prvalue를 사용하여 prvalue 유형의 객체를 초기화하면 임시가 구체화되지 않습니다. 할 때 return T();
prvalue를 통해 함수의 반환 값을 초기화합니다. 해당 함수가를 반환하므로 T
임시가 생성되지 않습니다. prvalue의 초기화는 단순히 반환 값을 직접 초기화합니다.
이해해야 할 것은 반환 값이 prvalue이므로 아직 객체 가 아니라는 것입니다 . 객체의 이니셜 라이저 일뿐 T()
입니다.
그렇게 T t = Func();
하면 반환 값의 prvalue가 개체를 직접 초기화합니다 t
. "임시 생성 및 복사 / 이동"단계는 없습니다. 이후 Func()
의 반환 값에 prvalue 동등하다 T()
, t
직접 초기화됩니다 T()
당신이했던 정확히 것처럼,T t = T()
.
prvalue가 다른 방식으로 사용되면 prvalue는 해당 표현식에서 사용되는 임시 객체를 구체화합니다 (또는 표현식이없는 경우 삭제됨). 그래서 만약 당신이했다면 const T &rt = Func();
, prvalue는 일반적인 임시 수명 연장 항목과 함께 T()
참조가에 저장 될 임시 ( 초기화 자로 사용) 를 구체화합니다 rt
.
제거가 보장되는 한 가지는 움직이지 않는 객체를 반환하는 것입니다. 예를 들면lock_guard
복사하거나 이동할 수 없으므로 값으로 반환하는 함수를 가질 수 없습니다. 그러나 보장 된 복사 제거 기능을 사용하면 가능합니다.
보장 된 제거는 직접 초기화에서도 작동합니다.
new T(FactoryFunction());
경우 FactoryFunction
반환 T
값에 의해,이 표현은 할당 된 메모리에 반환 값을 복사하지 않습니다. 대신 메모리를 할당 하고 할당 된 메모리 를 함수 호출에 대한 반환 값 메모리로 사용합니다.
따라서 값으로 반환하는 팩토리 함수는 알지 못해도 힙 할당 메모리를 직접 초기화 할 수 있습니다. 물론 이러한 기능이 내부적 으로 보장 된 복사 제거 규칙을 따르는 한. 그들은 유형의 prvalue를 반환해야합니다 T
.
물론 이것도 작동합니다.
new auto(FactoryFunction());
유형 이름 작성을 좋아하지 않는 경우.
위의 보장은 prvalue에 대해서만 작동한다는 것을 인식하는 것이 중요합니다. 즉, 명명 된 변수를 반환 할 때 보장 할 수 없습니다 .
T Func()
{
T t = ...;
...
return t;
}
이 경우 t
에도 액세스 가능한 복사 / 이동 생성자가 있어야합니다. 예, 컴파일러는 복사 / 이동을 최적화하도록 선택할 수 있습니다. 그러나 컴파일러는 여전히 액세스 가능한 복사 / 이동 생성자의 존재를 확인해야합니다.
따라서 NRVO (Named Return Value Optimization)에 대한 변경 사항은 없습니다.
std::function<T()>
정말로 ( 원샷)이라고 생각합니다.