template T와 ParamType T의 형식이 달라질 수 있으니 잘 숙지해야 한다. 특히 const와 &가 사용된 경우 그러하다.
기본적으로 템플릿과 동일하다. 단, { 1 } 형식의 값은 std::initializer_list가 된다.
template에서 decltype으로 인자와 동일한 형태를 반환하는 등의 방법으로 사용을 할 떄에는 좀 더 주의해야 한다.
IDE나 std::type_info::name이 항상 완벽하지는 않다. boost::typeindex::type_id_with_cvr은 정확하긴 하지만 내가 잘 알고있는 것이 최고다.
초기화 필수, 불필요한 복사 방지, 클로저 저장, 암묵적 변환 방지, 유지보수의 편리함 등의 이유로 auto를 사용하는 것이 좋다.
std::vector의 [] 접근 같은 대리자 클래스를 사용할 경우엔 static_cast으로 명시적으로 바꿔서 auto에 대입한다.
중괄호 초기화는 좋긴 하지만 std::initializer_list 매개변수가 있는 경우엔 가능하면 항상 이걸 선호하므로 주의가 필요하다. 선택과 스타일의 영역.
템플릿에 0이나 NULL을 넘기면 int 타입으로 인식해 문제가 생길 수 있다.
using은 typedef와 동일하게 동작하지만 템플릿을 작성하는 상황에 유리하다.
enum class는 전방 선언, 이름 오염, 암묵적 변환 방지 등의 장점이 있다.
예전에 사용하던 private 선언 후 구현하지 않는 방법은 = delete가 없어서 그랬던 것.
재정의 과정에서 실수 하거나 의도와 다르게 구현하는 것을 override와 final로 방지할 수 있다.
C++11에서는 const_iterator 사용이 쉬워졌기 때문에 안 쓸 이유가 없다.
이동, swap 등에서 noexcept를 사용하면 컴파일러 최적화 여지가 크다. 단, 한 번 사용하면 없애기 어려우므로 신중해야 한다.
객체와 함수가 다르게 동작하는 것을 알고, 계속 constexpr을 유지할 수 있다면 사용하는 것이 항상 좋다.
const 멤버 함수에서 의도적으로 mutable 멤버 변수를 조작할 경우 atomic, mutex 등을 이용해 스레드 안전하게 작성해야 한다.
소멸자, 복사, 이동 관련 함수 중 하나라도 사용자가 정의하면 이동 생성/대입은 자동 생성되지 않으므로 필요하면 = default를 통해 명시해 주는 것이 성능 하락을 방지할 수 있다.
unique_ptr에 소멸 함수를 직접 정의할 수도 있다.
shared_ptr은 제어 블록이 생성되며, 이 제어 블록이 복사되는 일은 없어야 한다. 이를 위해 std::enable_shared_from_this<> 라는 기반 클래스 탬플릿을 상속받기도 한다.
계층이 엄격히 분리되지 않는 구조에서 shared_ptr의 수명 주기에 따라 해당 shared_ptr을 관찰하는 포인터가 필요하다면 weak_ptr이 적합하다 (캐싱, 옵저버 패턴, 상호참조 방지 등).
코드, 속도, 메모리 사용량 면에서 효율적이다. 단, 커스텀 삭제자를 지정하거나 T의 크기가 매우 커서 weak_ptr의 사용이 끝나기까지 메모리를 붙잡고 있는게 싫은 경우엔 new를 사용하는게 더 합당할 수 있다.
unique_ptr을 사용할 경우엔 삭제자가 정의에 포함되기 때문에, 컴파일 시점에서 완전한 클래스여야 한다. 따라서 cpp 정의부로 특수 멤버 함수들의 구현을 옮겨 컴파일 에러를 피해야 한다.
move는 항상 오른값 캐스팅을, forward는 오른 값일 때만 오른 값 캐스팅을 수행한다.
형식 연역이 사용되는 곳(T&&, auto&&)에서 && 표시는 보편 참조를 뜻한다. 그 외에는 오른값 참조.
제곧내. 단, RVO의 대상이 될 수 있는 곳에 std::move를 사용하는 것은 나쁘다.
중복 적재를 할 경우 컴파일러가 생각하는 우선순위가 사람과 달라질 여지가 크다. 보편 참조가 다 잡아먹는다.
몇 가지 기법들을 소개하나 결국 최종 방법은 매우 복잡해져 사용시 재숙지가 필요학겠다.
하나라도 왼 값이 있으면 왼 값으로, 아니면 오른 값으로 축약된다.
탬플릿같은 경우 이동 연산이 없다고 생각하고 보수적으로 만들 필요도 있다.
형태가 모호한 값은 완벽 전달하지 못하는 경우가 있다.
갈무리 대상이 명확히 보이지 않아 실수하기 쉽다. 특히 기본 값 갈무리는 람다가 자기 완결 적이라는 착각을 하게 만든다.
C++ 11은 초기화 갈무리를 지원하지 않기 떄문에 std::bind를 사용해서 우회하는 방법을 사용할 수 있다.
제곧내. decltype이 완벽히 타입을 유추할 수 있다.
가독성이 더 뛰어나고, 복사/이동 동작이 코드에 드러나며 컴파일러 인라인화를 통해 성능도 향상될 여지가 있다.
직접 스레드를 다루는 것에 들어가는 구현 비용을 C++ 라이브러리 구현자들에게 위임할 수 있다. 단, 네이티브 핸들을 사용해 스레드 우선선위 지정 등의 일을 해야 하는 경우 직접 스레드 기반 프로그래밍을 해야 하는 경우도 있다.
std::async의 기본 사용은 비동기/동기 실행을 모두 허용하기 때문. 비동기로 실행될 경우에 대한 예외 등을 철저히 작성하거나 강제로 비동기로 실행할 수 있다.
합류 가능한 상태로 스레드가 종료한다면 프로그램이 종료된다. RAII 방식을 이용해 소멸자에서 합류 가능한 상태이면 join/detach 해주도록 만들 수 있다. 이 경우 std::thread 객체는 마지막에 선언하는게 안전하다.
std::async로 생성한 std::future는 자신이 공유 상태를 참조하는 마지막 미래 객체라면 과제가 완료될 때까지 소멸자가 불리지 않는다. (암묵적 join 호출)
std::promise와 std::future(std::shared_future)를 사용하면 뮤택스나 스레드 낭비 없이 단발성 사건 알림을 구현할 수 있다.
둘은 용도가 전혀 다르다. volatile의 경우 컴파일러가 임의로 메모리에 대한 읽기/쓰기를 최적화 하지 않도록 강제한다. 메모리에 값을 쓰는 것 자체가 통신 등의 목적을 가지고 있는 특수한 경우에 사용할 수 있다.
이동 생성이 가능해졌기 때문에 이동 비용이 저렴하다면 값 전달도 고려해봄직 하다. 구현이 훨씬 간단하다.
이론적으로 생성 삽입의 속도가 더 떨어질 일은 없다.