목록프로그래밍 (42)
INDIES
SFINAE템플릿 메타 프로그래밍의 기본이 되는 기법. Substitution Failure Is Not An Error의 줄임말이다. 다짜고짜 SFINAE부터 다루면 너무 어려우니 왜 이 개념이 필요하고 어떤 때에 쓰이는지 차근차근 살펴보자. SFINAE는 함수 템플릿과 관련하여 나타나는 특성이기 때문에 함수 템플릿의 컴파일 과정에 대해 기본적인 개념이 있어야 이해하기가 쉽다.함수 템플릿의 컴파일 과정과 관련된 아래 주제들은 다 하나하나 상세히 파고 들어가면 굉장히 복잡하고 알아야 할 내용이 많지만, SFINAE가 주제지 함수 템플릿이 주제가 아니므로 함수 템플릿과 관련한 내용은 기초적인 개념만 간단히 서술했다. 혹 함수 템플릿의 컴파일 과정에 대한 상세한 내용이 궁금하신 분들은 따로 더 찾아보시는 것..
universal reference 오버로딩 회피법오버로딩 회피말 그대로 오버로딩 자체를 안 해버리는 것이다. item 26을 기준으로 하자면, logAndAdd 함수에서 인자로 universal reference를 받는 경우는 함수 이름을 logAndAddName으로, index를 인자로 받는 경우는 함수 이름을 logAndAddNameIdx로 짓는 것이다. 이렇게 하면 애초에 오버로딩 자체가 안 일어나므로 아무런 문제 없이 잘 해결된다. 단, 이 해결법은 item 26에서도 언급된 클래스 생성자 관련 문제에는 사용할 수 없다는 단점이 있다.Pass By const T&또 다른 해결책은 universal reference를 쓰지 않고, C++ 98때처럼 const T&타입을 넘기는 것이다. 이렇게 하면..
Universal Reference는 오버로딩을 피해라이름을 인자로 받아서, 로그를 남기고 해당 이름을 어떤 전역 자료구조에 저장하는 함수를 생각해보자.std::multiset names; void logAndAdd(const std::string& name) { auto now = std::chrono::system_clock::now(); log(now, "logAndAdd"); names.emplace(name); }위 코드는 동작은 정상적으로 하지만, 별로 효율적인 코드는 아니다. 이 함수를 호출할 때 있을 수 있는 3가지 경우를 생각해보자.std::string petName("Darla"); //case 1 : lvalue std::string 넘기는 경우 logAndAdd(petName); /..
std::move, std::forward의 바른 사용적합한 위치에 쓰기class Widget { public: Widget(Widget&& rhs) : name(std::move(rhs.name)), p(std::move(rhs.p)) { ... } ... private: std::string name; std::shared_ptr p; };rvalue reference는 이동(move)될 수 있는 객체를 말한다. 반드시 이동되는 것은 아니지만 어쨌든 이동될 가능성은 있다는 뜻이다. 그리고 이렇게 rvalue reference로 인자를 넘겨서 move를 통해 멤버들을 초기화하는 경우는 그 목적에 따라 std::move를 쓰는 게 적합할 것이다.반면에 universal reference를 쓰는 경우엔, ..
universal reference, rvalue reference어떤 타입 T에 대한 rvalue reference는 T&&로 나타낸다. 따라서 소스 코드에 나타나는 T&&들은 모두 T 타입에 대한 rvalue reference로 봐도 될 것이다. 진짜 그럴까? 안타깝게도 실제로는 전혀 그렇지 않다.//rvalue reference void f(Widget&& param); //rvalue reference Widget&& var1 = Widget(); //not rvalue reference auto&& var2 = var1; //rvalue reference template void f (std::vector&& param); //not rvalue reference template void f(T..
std::move, std::forward 이해하기std::move와 std::forward에 대해 가장 먼저 알아야 할 점은, 이들 함수는 실제로 어떤 동작을 수행하지 않는다는 것이다. std::move가 실제로 객체를 move시키지 않고, std::forward도 실제로 객체를 forward하지 않는다. 이들 함수는 단순히 캐스팅을 수행하는 함수(함수 템플릿)일 뿐이다. std::move는 무조건 인자로 넘어온 값을 rvalue reference로 캐스팅해주고, std::forward는 특정한 조건이 만족될 경우에만 인자로 넘어오는 값을 rvalue reference로 캐스팅해준다.std::move먼저 std::move의 동작부터 이해해보자. std::move의 동작을 간략하게 구현하면 아래와 같이 ..
pImpl idiompImpl(pointer to Implementation) idiom은 프로그램 빌드에 걸리는 시간을 줄여주기 위한 기법중 하나다. 클래스의 데이터 멤버를 구현 객체(implementation class or struct)에 옮긴 후 그걸 가리키는 포인터를 멤버로 들고 있게 만드는 것이다. 예를 들어, Widget 클래스가 다음과 같이 선언되어 있다고 하자.class Widget { public: Widget(); private: std::string name; std::vector data; Gadget g1, g2, g3; };Widget은 멤버로 std::string, std::vector, Gadget 타입을 갖고 있기 때문에 , , "Gadget.h" 세 개의 파일을 incl..
make_shared, make_uniquestd::make_shared와 std::make_unique는 각각 shared_ptr과 unique_ptr을 만들기 위해 사용되는 템플릿 함수들이다. 근데 왜인지는 몰라도 std::make_shared는 C++ 11 표준에 포함이 되어있는데 std::make_unique는 C++ 11표준에 포함이 안 돼있다. std::make_unique는 C++ 14부터 표준에 들어가는데, C++ 11 표준에서 std::make_unique를 쓰고 싶다면 아래의 간단한 코드를 이용하면 된다.template std::unique_ptr make_unique(Ts&&... params) { return std::unique_ptr(new T(std::forward(params..
std::weak_ptr의 사용std::shared_ptr처럼 동작하지만 막상 레퍼런스 카운팅은 되지 않는 포인터가 유용하게 쓰일 때가 있다. 이런 포인터를 std::weak_ptr이라고 부른다. std::weak_ptr은 역참조(dereference)도 안 되고 nullptr인지 아닌지 테스트하는 것도 안 된다. std::weak_ptr은 그 자체로 홀로 쓰이는 스마트 포인터가 아니기 때문이다. 이까지만 들으면 당최 이 놈이 어디서 유용하다는 것인지 전혀 이해가 안 될 수 있다. 하지만 std::weak_ptr은 굉장히 유용한 스마트 포인터이다. 어떤 경우에 활용될 수 있는지 하나씩 살펴보자.dangling 감지std::weak_ptr은 이미 파괴됐을 가능성이 있는 자원을 참조해야할 경우가 있을 때 해..
공유 자원 관리에는 std::shared_ptr요즘 나오는 언어들은 거의 대부분 garbage collection 기능을 갖고 있다. 반면 C++은 언어 차원에서 garbage collection을 지원해주지 않기 때문에 사용한 자원을 일일히 해제해줘야하는 어려움이 있었는데, 이런 어려움을 해결하기 위해 등장한 것이 shared_ptr이다. shared_ptr은 garbage collection처럼 여럿이 공유하는 자원을 더 이상 필요없을 때 자동으로 해제해줄 뿐만 아니라, 어느 시점에 해제되는지도 알 수 있다(garbage collection은 자원이 정확히 어느 시점에 해제되는지 알기 힘들다. 자동으로 일정 규칙에 따라 관리하다가 해제할 때가 되면 해제하기 때문에).shared_ptr의 동작 원리는 ..