본문 바로가기

Computer Science/C++6

충격!) C++에서 Rust의 블록 표현식과 유사한 구문을 사용할 수 있다?! Rust는 {}으로 감싸지는 코드 블록이 단순한 문장들의 나열이 아니라 하나의 표현식(Expression)으로 사용될 수 있다. let x = ??? let result = { let x0 = if x >= 0 {x} else {0}; x0 * x0 * x0 + 3 * x0 }; 코드블록의 마지막 문장인 x0 * x0 * x0 + 3 * x0 뒤에 세미콜론이 찍혀 있지 않으므로, 코드블록은 x0 * x0 * x0 + 3 * x0으로 평가되는 표현식(Expression)이 된다. 따라서 result에 해당 값이 들어간다. 이런 구문은 이름공간의 오염을 줄이는 역할을 한다. result를 계산할 때 말고는 절대로 x0가 다시 사용되지 않는다는 것을 알고 있다면 코드 블록을 사용하여 x0을 특정 영역 안에 가.. 2024. 3. 4.
[C++] 객체지향, 제네릭 세그먼트 트리 라이브러리 구현하기 이 글에서는 재사용 가능한 제네릭 세그먼트 트리 라이브러리를 구축하는 방법에 대해 알아본다. 세그먼트 트리를 사용할 어떤 상황이 나와도 최대한 복붙만으로 문제를 해결할 수 있게 하는 것을 목표로 한다. 세그먼트 트리의 작동 원리 자체에 대해 설명하는 글은 아니기 때문에 독자가 세그먼트 트리에 대해 어느 정도 이해하고 있다고 가정한다. Motivation 세그먼트 트리는 point update & range query를 O(log N)에 처리할 때 유용하게 사용 가능한 자료구조다. 여기서 range query는 다음과 같은 것들이 주어질 수 있다: 구간 a[l..r]의 합/곱/xor을 구하기 구간 a[l..r]의 최솟값/최댓값을 구하기 구간 a[l..r]의 최대 부분연속합을 구하기 (속칭 금광세그) 구간 .. 2023. 10. 3.
C++ 버전이 바뀌면서 의미가 달라진 키워드들 C++에는 이렇게 많은 키워드가 있다. C++은 C에서 처음 시작했지만 시대가 발전하면서 Python, Haskell 등 프로그래밍의 패러다임을 바꾼 수많은 언어가 튀어나왔고 C++는 30년 동안 그 수많은 패러다임을 다 먹어치울 기세로 커졌기 때문이다. 그 과정에서 C++의 몇몇 키워드는 의미가 바뀌기도 했다. 가장 대표적인 키워드가 inline으로, 이 키워드는 C++에서 함수 실행 오버헤드를 줄이기 위해 함수 호출하는 부분을 그냥 함수 본문으로 대체하는 것과는 전혀 관련이 없다. 모든 C++ 개발자들은 바뀐 키워드의 내용을 숙지하여 모던한 프로그래밍 습관을 기르도록 하자. auto (C에서) storage duration을 auto로 설정함 (C++11부터) 정의된 변수의 타입을 컴파일러가 알아서 .. 2023. 1. 9.
[C++] TMP로 컨테이너 내부 타입 정보 얻어오기 [템플릿 메타프로그래밍] Motivation 제네릭 프로그래밍을 하고 싶다고 하자. std::vector와 같은 컨테이너 내부의 타입이 몇 바이트인지 확인할 일이 생겼다고 가정해 보자. 컨테이너로 std::vector를 사용한다는 사실을 미리 알고 있다면, 다음과 같이 짜는 것으로 충분하다. sizeof(typename std::vector::value_type) 사실은 이렇게 할 필요도 없으며, 이걸로도 충분하다. sizeof(T) 문제는 우리가 컨테이너로 std::vector가 들어온다는 사실을 모를 때 발생한다. 이렇게 가정해보자. "어떤 타입 X가 들어오는데, 우리는 X가 std::vector 꼴인지 (some custom container) 꼴인지도 모르고 사실 컨테이너인지도 모른다." 이럴 때는 다음의 해결 방법이 우.. 2022. 10. 22.
[C++] 프록시(Proxy) 디자인 패턴 (feat. bitset) 프록시(Proxy)는 대리자라는 뜻을 갖고 있다. 즉 어떤 객체가 하는 일을 그대로 흉내내는 객체이다. C++에서 프록시의 가장 유명한 예시는 바로 스마트 포인터(Smart Pointer)일 것이다. 스마트 포인터는 자신이 소멸될 때 delete 연산자를 알아서 호출해서 메모리 누수를 막는 똑똑한 객체인데, 당연하지만 스마트 포인터 그 자체는 포인터가 아니라 객체이다. 그럼에도 스마트 포인터는 일단 포인터가 할 수 있는 모든 일을 할 수 있도록 설계되어 있다(unique_ptr 같은 경우 복사 연산과 복사 대입 연산이 안 되기는 하지만). 특히 * 연산자와 -> 연산자를 제공하므로 사용자는 스마트 포인터를 그냥 포인터 쓰듯이 쾌적하게 사용할 수 있을 것이다. 이 글에서는 비트셋(bitset)에서 프록시 .. 2022. 9. 18.
C++ Dynamic Programming 꿀팁 다음과 같이 코드를 짜면, memoization DP를 전역 변수 없이, 불필요하게 많은 함수 인자 없이 안전하게 짤 수 있다. #include int main() { std::vector memo(100, std::optional()); std::function fibonacci = [&fibonacci, &memo](uint32_t i) -> int { if (memo[i].has_value()) { return memo[i].value(); } return i 2022. 8. 23.