본문 바로가기

Programming

이펙티브 모던 C++_5

[Effective Modern C++] 2015 스콧 마이어스 저
" C++11 과 C++ 14 를 효과적으로 사용하는 42가지 방법

5장 오른값 참조, 이동 의미론, 완벽 전달

 

 

항목 23. std::move와 std::forward 를 숙지하라

: std::move는 이동하는것이 아니라 사실상 static_cast만 수행한다.

 

기술적으로 std::forward만 사용해도 문제는 없지만 타자량, 잘못된 형식을 지정하는 실수를 방지한다는 점에서 std::move 사용도 지향된다. 

 

1. 이동을 지원할 객체는 const로 선언하지 말아야 한다. 복사 연산으로 변환된다.

2. std::move는 rvalue로의 캐스팅만 수행, std::forward는 rlvalue인 경우에만 수행

 

 


 

항목 24. 보편 참조와 오른값 참조를 구별하라

T&& : rvalue 일 수도 있고 lvalue일 수도 있다. 

 

1. 함수 템플릿 매개변수의 형식이 T&& 형식이고 T가 연역된다면 그 매개변수나 객체는 보편 참조이다.

2. 형식 선언의 형태가 정확히 형식&&이 아니거나 형식 연역이 일어나지 않으면 rvalue를 뜻한다.

3. rvalue로 초기화되는 보편 참조는 rvalue 참조이고 lvalue로 초기화되는 보편 참조는 lvalue 참조에 해당한다.

 

 


 

 

항목 25. 오른값 참조에는 std::move를, 보편 참조에는 std::forward를 사용하라

보편 참조는 이동에 적합할 수도 있고 아닐 수도 있다. 보편 참조는 rvalue로 초기화되는 경우에만 캐스팅되어야 한다. 그래서 보편 참조에는 std::forward를 사용한다.

 

1.

결과를 값 전달 방식으로 돌려주는 함수가 rvalue 참조 : std::move

보편 참조를 돌려줄 때 : std::forward를 적용하라

2. 반환값 최적화의 대상이 될 수 있는 지역 객체에는 절대 std::move나 std::forward를 적용하지 말아야 한다.

: 오히려 최적화를 방해한다.

RVO
함수내에서 반환 값을 함수 메모리가 아니라 반환 값 메모리에 바로 적재함으로써 최적화하는 기법
C++11 이후 표준으로 제정되어 반환값에 대한 최적화를 수행한다.


https://post.naver.com/viewer/postView.nhn?volumeNo=9735713&memberNo=559061 참조

 


 

 

항목 26. 보편 참조에 대한 중복적재를 피하라

*중복 적재 

: 오버로딩(with 보편 참조)

1. 보편 참조에 대한 중복 적재는 거의 항상 보편 참조,  중복 적재 버전이 예상보다 자주 호출되는 상황으로 이어진다.

2. 완벽 전달 생성자들은 문제가 많다. 그런 생성자는 대체로 비const 왼 값에 대한 복사 생성자보다 더 나은 부합이며, 기반 클래스 복사 및 이동 생성자들에 대한 파생 클래스의 호출들을 가로챌 수 있기 때문이다.

   
   //보편 참조 버전
   template<typename T>
    void logAndAdd(T && name)
    {
        auto now = std:; chrono::system_clock::now();
        log(now, "logAndAdd");
        names.emplace(std:; forward<T>(name));
    }

	//int 오버로딩 함수
    void logAndAdd(int idx)
    {
        auto now = std::chrono::system_clock::now();
        log(now, "logAndAdd");
        names.emplace(nameFromIdx(idx));
    }

	// short nameIdx는 보편 참조 버전을 사용할까? int 오버로딩 함수를 사용할까?
    // 컴파일러는 승격보다 정확한 부합을 더 우선순위 처리하기 때문에 
    // (예기치 않은)보편 참조를 사용하게 되고 보편참조는 string을 고려하고 있기 때문에 에러가 발생한다.
    short nameIdx;
    logAndAdd(nameIdx);

 

 


 

항목 27. 보편 참조에 대한 중복적재 대신 사용할 수 있는 기법들을 알아두라

1. 중복 적재를 포기한다. (함수 이름을 다르게 한다던가)

2. const T& 매개변수를 사용한다. 

3. 값 전달 방식의 매개변수를 사용한다.(1번과 비슷한 이유로 아예 정확히 타입을 명시한다.)

4. 꼬리표 배분을 사용한다. (중간 함수를 둬서 우리가 원하는 방식으로 제어한다.)

5. 보편 참조를 받는 템플릿을 제한한다.

- std::enable_if 라는 것이 존재하는데 이는 컴파일러에게 특정 템플릿이 존재하지 않는 것처럼 행동하게 만들 수 있다. 

6. 절충점들 (assert를 이용하여 금지된 타입들을 확인할 수 있게 한다.)

 

 


 

항목 28. 참조 축약을 숙지하라

: 참조의 참조의 참조...가 생기면(&& & && T)그 값은 하나의 참조(&T)가 된다.

: 만일 두 참조 중 하나라도 lvalue 참조이면 결과는 lvalue이고 그렇지 않으면 rvalue이다.

참조 축약이 일어나는 경우

1. 템플릿 인스턴스화

2, auto 변수에 대한 형식 연역

3. typedef 사용

4. decltype 사용

 

 


 

항목 29. 이동 연산이 존재하지 않고, 저렴하지 않고, 적용되지 않는다고 가정하라

- 지원하지 않는 경우가 많다

- 이동과 복사의 비용이 비슷할 수 있다.

1. 이동 연산이 없다.

2, 이동 연산이 더 빠르지 않다.

3. 이동 연산을 사용할 수 없다.

 

 


 

 

항목 30. 완벽 전달이 실패하는 경우들을 잘 알아두라

: 말 그대로 주소값, 특성 모두 완벽하게 전달되는 것

1. 완벽 전달은 템플릿 형식 연역이 실패하거나 틀린 형식을 연역했을 때 실패한다. (의도치 않은 타입으로 연역)

2.

인수가 중괄호 초기치이거나 0 또는 NULL로 표현된 널 포인터(NULL이라면 int로 완벽 전달이 이루어질 수 있다.)

선언만 된 정수 static_const 및 constexpr 자료 멤버, 템플릿 및 중복 적재된 함수 이름, 비트 필드이면 완벽 전달이 실패할 수 있다.

'Programming' 카테고리의 다른 글

이펙티브 모던 C++_7  (0) 2020.12.13
이펙티브 모던 C++_6  (0) 2020.12.06
이펙티브 모던 C++_4  (0) 2020.11.22
이펙티브 모던 C++_3  (0) 2020.11.15
이펙티브 모던 C++_2  (0) 2020.11.08