본문 바로가기

Programming

이펙티브 모던 C++_6

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

6장 람다 표현식

람다 표현식 :
클로저 : 람다에 의해 만들어진 실행 시점 객체(복사, 참조)
클래스 클로저: 클로저를 만드는 데 쓰인 클래스

 

 

항목 31 기본 갈무리 모드를 피하라

-갈무리는 값, 참조가 대상을 잃을 위험이 있다. (대상 값이 변경/지워진 후 람다가 실행될 경우)

-람다는 바로 사용하는 것이 좋다.

-람다는 생성된 범위 내에 보이는 지역 변수에만 해당된다.

class Widget {
public:
  ...
  void addFilter() const;
  
private:
  int divisor;
};

void Widget::addFilter() const
{
    // [=]
    // [this] 를 넣은 것과 같음
    filters.emplace_back(
    [=](int value) { return value % divisor == 0; } // 컴파일 에러 x
  };
  
  // 캡쳐된 범위가 없음
    filters.emplace_back(
    [](int value) { return value % divisor == 0; } // 컴파일 에러 o
  };
  
  
    // this를 잃을 것이 걱정된다면? 지역변수에 저장하여 캡쳐한다.
    auto divisorI = this.divisor;
      filters.emplace_back(
    [divisor](int value) { return value % divisorI == 0; } // 컴파일 에러 x
  };
}

 

 

항목32. 객체를 클로저 안으로 이동하려면 초기화 갈무리를 사용하라.

auto func = [pw = std::make_unique<Widget>()]
            { return pw->isValidated()
                     && pw->isArchived(); };

 

 

항목 33. std::forward를 통해서 전달할 auto&& 매개변수에는 decltype을 사용하라

// 람다에서 오토로 받은 항목을 forward로 전달하려면 decltype을 사용한다.
auto f = [](auto&& x)
         //{ return normalize(std::forward<???>(x)); };
         { return normalize(std::forward<decltype(x)>(x)); };

// 여러개의 매개변수를 전달할 경우
auto f = [](auto&& ...x)
         { return normalize(std::forward<decltype(x)>(x)...); };

 

 

항목 34. std::bind 보다 람다를 선호하라

1. 람다에서는 호출되는 시점에 내부 함수를 호출, bind는 bind 객체가 생성된 시점에 내부 함수를 호출한다.

auto setSoundL =
  [](Sound s)
  {
    using namesapce std::chrono;
    using namespace std::literals;
    
    setAlarm(steady_clock::now() + 1h, // 호출 시점의 now
             s,
             30s);
  };
  
  auto setSoundB =
  std::bind(setAlaram,
            steady_clock::now() + 1h, // bind 시점의 now
            _1,
            30s);

2. 오버 로딩 함수를 담을 경우 bind함수는 오버 로딩 함수를 구분하지 못하여 함수 타입을 따로 명시하여 보여주어야 한다.

void setAlaram(int a, int b, int c)
void setAlaram(int a, int b, int c, int d)

auto setSoundB =
  std::bind(setAlaram, // 어떤 함수를 호출해야할 지 모르게 된다.
            steady_clock::now() + 1h,
            _1,
            30s);


// 함수 타입을 명시하면 부를 수 있게 된다.
using SetAlram3ParamType = void(*)(Time t, Sound s, Duration d);

auto setSoundB =
  std::bind(static_cast<SetAlram3ParamType>(setAlarm),
            std::bind(std::plus<>(),
                      std::bind(steady_clock::now),
                      1h),
            _1,
            30s);

 

 


 

7장 동시성 API

1. 스레드 기반 프로그래밍보다 과제 기반 프로그래밍을 선호하라

스레드 기반 : std::thread
과제 기반 : std::async(doAsyncWord)

- 과제 기반의 경우 반환 값을 얻을 수 있다.

- 소프트웨어 스레드가 하드웨어 스레드보다 많아질 경우 과다 구독이 발생한다.

- 그렇게 되면 switch가 발생하는 상황이 많아지고 CPU 캐시는 히트되지 않게 되고 다음 스레드가 사용할 캐시도 오염되는 문제가 생긴다.

- std::aync의 경우 스레드를 생성하지 않을 수 있다는 차이점이 있다.

- 하지만 스레드를 생성하지 않을 수 있기 때문에 이에 대한 방책을 마련해두어야 한다.

 

항목 36. 비동기성이 필수일 때에는 std::launch::async를 지정하라

std::async
std::task 클래스 기반으로 만들어진 클래스
std::asyn는 std::thread와 달리 내부적으로 thread Pool을 만들어 관리한다.
std::future <void> = std::async(std::launch::asyn, func) // launch 바로 실행한다는 뜻
std::future <void> = std::async(std::launch::deferred, func) // get, wait 호출될 때만 실행한다는 뜻

// std::launch를 사용하여 실행 시점을 지정하는 것을 기본으로 한다.

// 그렇지 않으면 지연되어서 실행되는지 언젠가 실행될 수 있는 것인지 실행될 수는 있는지 알 수없게 된다.

 

1. 과제의 비동기적 실행과 동기적 실행을 모두 허용한다.

2. 이런 유연성 때문에 thread_local 접근의 불확실성이 발생하고 과제가 실행되지 않을 수도 있고 시간 만료 기반 wait 호출에 대한 프로그램 논리에도 영향이 미친다.

3. 과제를 반드시 비동기적으로 실행해야 한다면 std::launch::async를 지정하라

'Programming' 카테고리의 다른 글

이펙티브 모던 C++_8  (0) 2020.12.20
이펙티브 모던 C++_7  (0) 2020.12.13
이펙티브 모던 C++_5  (0) 2020.11.29
이펙티브 모던 C++_4  (0) 2020.11.22
이펙티브 모던 C++_3  (0) 2020.11.15