Books/Effective Modern C++
[Effective Modern C++] 16. const멤버 함수를 스레드에 안전하게 작성하자
[Effective Modern C++] 16. const멤버 함수를 스레드에 안전하게 작성하자
2022.09.11멤버 함수가 멤버 변수들을 수정하지 않는다면 const로 선언하는 것이 자연스럽다. 그런데 스레드를 사용하면 문제가 생길 수 있다. 아래의 예시를 봐보자. mutex 사용 다음은 다항식의 근을 구하는 함수 roots를 구현하는 내용이다. 성능 향상을 위해 캐싱을 이용하였으며, rootsAreValid의 값을 이용하여 캐싱 여부를 판별한다. class Polynomial { public: using RootsType = std::vector; RootsType roots() const { if(!rootsAreValid) { ... rootsAreValid = true; } } private: mutable bool rootsAreValid { false }; mutable RootsType rootVal..
[Effective Modern C++] 15. 가능하면 항상 constexpr을 사용하자
[Effective Modern C++] 15. 가능하면 항상 constexpr을 사용하자
2022.09.11constexpr은 객체에 사용하면 const의 향상된 버전이지만 함수에 사용하면 다른 의미를 가진다. 그러나 일반적으로 constexpr이 나타내는 의미는 컴파일 시간에 알려진 값을 나타낸다. constexpr 객체 constexpr은 객체를 const로 만들고, LiteralType(값이 알려져 있고 컴파일 시점에 사용할 수 있는 객체)으로 만든다. 이렇게 컴파일 중에 알려진 값은 여러 권한이 있는데 예를 들어 읽기 전용 메모리에 저장될 수 있으며 불변의 상수 표현식이 필요할 때 대신 사용할 수도 있다.(배열 크기나 템플릿 매개변수(std::array 객체 길이 포함), 열거형 멤버의 값, 정렬 지정자 등이 포함된다.) 이러한 작업을 수행하기 위해 변수를 사용하려는 경우 컴파일러에서 컴파일 시점의 값..
[Effective Modern C++] 14. 예외를 방출하지 않을 함수는 noexcept로 선언하자
[Effective Modern C++] 14. 예외를 방출하지 않을 함수는 noexcept로 선언하자
2022.09.04noexcept는 함수 인터페이스의 일부이다. 이는 호출자가 noexcept 여부에 의존할 수 있음을 뜻한다. C++98에서 예외 지정은 함수 구현이 바뀌면 예외 지정도 바뀔 가능성이 있었고, 기존의 예외 지정에 의존하던 클라이언트 코드는 깨질 수도 있어 예외 지정이 득보다 실이 크다고 판단되었다. 하지만 C++11에서, 함수의 예외 방출 행동에 관해 정말로 의미 있는 정보는 함수가 예외를 하나라도 던질 수 있는지 아니면 절대로 던지지 않는지의 여부라는 점이라는 의견이 중요해졌는데, 본질적으로 C++98의 것을 대체하는 C++11의 예외 지정에는 바로 그러한 흑백논리가 깔려 있다.(C++98 스타일의 예외 지정도 여전히 유효하나, 비권장(deprecate) 기능으로 분류되었다) 결과적으로, C++11에서..
[Effective Modern C++] 13. iterator보다 const_iterator를 선호하자
[Effective Modern C++] 13. iterator보다 const_iterator를 선호하자
2022.09.04C++98에서 const_iterator는 그리 실용적이지 않았다. 왜냐면, 비상수 컨테이너로부터 const_iterator를 얻는 방법도 까다로웠고, 삽입과 삭제 위치를 iterator로만 지정할 수 있었기 때문이다. 하지만 C++11에서는 C++98에서의 문제점들이 개선되었다. 따라서 C++11이상의 환경에서 프로그래밍을 한다면 가능한 iterator보다 const_itertaor를 사용하는 것이 좋다. // 예를 들어 std::vector에서 1983이라는 값이 처음 나오는 지점을 찾고 그 곳에 1998이라는 값을 삽입한다고 하자. // 벡터에 1983이 하나도 없으면 1998을 벡터의 끝에 삽입해야 한다. // C++98에서는 iterator를 사용하여 간단하게 구현할 수 있다. std::vect..
[Effective Modern C++] 12. 재정의 함수들은 override로 선언하자
[Effective Modern C++] 12. 재정의 함수들은 override로 선언하자
2022.09.04재정의가 일어나려면 다음과 같은 여러 필수조건을 만족해야 한다. 기반 클래스 함수가 반드시 가상 함수이어야 한다. 기반 함수와 파생 함수의 이름이 반드시 동일해야 한다(단, 소멸자는 제외). 기반 함수와 파생 함수의 매개변수 타입들이 반드시 동일해야 한다. 기반 함수와 파생 함수의 const성이 반드시 동일해야 한다. 기반 함수와 파생 함수의 반환 타입과 예외 지정(exception specification)이 반드시 호환되어야 한다. [C++11에서 추가된 조건] 멤버 함수들의 참조 한정사(reference qualifier)들이 반드시 동일해야 한다. 이러한 모든 재정의 요구조건들이 뜻하는 것은, 작은 실수가 큰 차이를 빚을 수 있다는 것이다. 재정의 실수가 포함된 코드는 프로그래머가 의도한 것과는 ..
[Effective Modern C++] 11. 정의되지 않은 비공개 함수보다 삭제된 함수를 선호하자
[Effective Modern C++] 11. 정의되지 않은 비공개 함수보다 삭제된 함수를 선호하자
2022.09.03C++98에서는 어떤 함수의 사용을 막고 싶으면, private으로 선언하고 함수의 정의를 만들지 않는 방법을 사용하였다. // C++98 template class basic_ios : public ios_base { public: ... private: basic_ios(const basic_ios& ); // 정의 안함 basic_ios& operator=(const basic_ios&); // 정의 안함 }; 이렇게 private로 선언하면 클라이언트가 함수를 호출할 수 없다. 즉, 멤버 함수나 friend클래스가 정의하지 않은 함수를 사용하면 링크는 함수 정의 누락으로 인해 실패한다. C++11에서는 더 좋은 방법이 있는데, 사용을 막을 함수 선언의 끝에 "=delete"를 붙이면 된다. 이러한..
[Effective Modern C++] 10. 범위 없는 enum보다 범위 있는 enum을 선호하자
[Effective Modern C++] 10. 범위 없는 enum보다 범위 있는 enum을 선호하자
2022.08.28C++98 스타일의 enum을 이제는 범위 없는 enum이라고 부른다. C++98 스타일의 enum으로 선언된 열거자들에 대해서는 "한 중괄호 쌍 안에서 어떤 이름을 선언하면 그 이름의 가시성은 해당 중괄호 쌍이 정의하는 범위로 한정된다"는 일반적인 규칙이 적용되지 않는다. 그런 열거자 이름들은 enum을 포함하는 범위에 속하며, 따라서 그 범위에 같은 이름이 있으면 안된다. 이러한 이유로 C++98스타일인 enum은 범위 없는 enum이라고 부른다. enum Color { black, white, red }; // black, white, red는 Color가 속한 범위에 속함 auto white = false; // 오류! 이 범위에 이미 white가 선언되어 있음 범위 있은 enum의 열거자들은 그..
[Effective Modern C++] 9. typedef보다 별칭 선언을 선호하자
[Effective Modern C++] 9. typedef보다 별칭 선언을 선호하자
2022.08.28아래처럼 typedef는 템플릿화를 지원하지 않지만 별칭 선언은 지원한다. template // MyAllocList::type은 std::list와 동의어이다. struct MyAllocList { typedef std::list type; }; MyAllocList::type lw; // 클라이언트 코드 // 별칭 템플릿은 훨씬 더 간단하고, 직접적으로 표현할 수 있다. template // MyAllocList::type은 std::list와 동의어이다. using MyAllocList = std::list; MyAllocList lw; // 클라이언트 코드 또 별칭 템플릿에서는 "::type" 접미어를 붙일 필요가 없다. 그래서 C++11의 모든 타입 변환에 대한 별칭 템플릿 버전들을 C++14에..
[Effective Modern C++] 8. 0과 NULL보다 nullptr을 사용하자
[Effective Modern C++] 8. 0과 NULL보다 nullptr을 사용하자
2022.08.28리터럴 0은 int이지 포인터가 아니다. 실용적인 관점에서는 NULL도 마찬가지이다. 그러나 NULL의 경우에는 다소 불확실한 세부사항이 존재한다. 구현이 NULL에 int 이외의 정수 타입을 부여할 수도 있기 때문이다. 이 점이 이야기하는 주된 문제는 포인터와 정수 타입에 대한 오버로딩이 의외의 방식으로 해소된다는 점이었다. 0이나 NULL로 그런 오버로딩 된 함수를 호출했을 때, 포인터를 받는 오버로딩된 함수가 호출되는 일은 없다. 반면 nullptr을 사용하면 오버로딩이 예상과 다르게 해소되는 일이 없다. 그 뿐만 아니라, nullptr는 코드의 명확성도 높여준다. 템플릿의 타입 추론시에도, 0과 NULL은 정수 타입으로 추론하는 반면 nullptr은 포인터 타입으롤 추론한다. // f의 세 가지 ..
[Effective Modern C++] 7. 객체 생성 시 괄호(())와 중괄호({})를 구분하자
[Effective Modern C++] 7. 객체 생성 시 괄호(())와 중괄호({})를 구분하자
2022.08.28중괄호 초기화(균일화 초기화) 변수 초기화 방법은 기존의 초기화 방법에, 중괄호 초기화 방법을 더해 총 4가지 방법으로 변수를 초기화 할 수 있다. int a(0); // 초기치를 괄호로 감싼 예 int b = 0; // 초기치를 "=" 다음에 지정한 예 int c{ 0 }; // 초기치를 중괄호로 감싼 예 int d = { 0 }; // "="와 중괄호로 초기치를 지정한 예 이번 항목의 나머지 부분에서는 이러한 등호와 중괄호 구문은 무시한다. 대체로 C++은 이를 중괄호만 사용한 구문과 동일하게 취급하기 때문이다. 중괄호 초기화를 이용하면 이전에는 표현할 수 없었던 방식의 객체 생성을 표현할 수 있다. std::vector v{ 1, 3, 5 }; // v의 초기 내용은 1, 3, 5 중괄호 구문은 비..
[Effective Modern C++] 6. auto가 원치 않은 타입으로 추론 될 때에는 명시적 타입의 초기화를 생각하자
[Effective Modern C++] 6. auto가 원치 않은 타입으로 추론 될 때에는 명시적 타입의 초기화를 생각하자
2022.08.27일단, 보이지 않는 프록시 타입 때문에 auto가 초기화 표현식의 타입을 잘못 추론할 수 있다는 점을 알아야한다. 예를 들어, std::vector::operator[]의 반환 타입은 bool&이 아니라 어떤 프록시 클래스 타입이다. 프록시 클래스 중에는 사용자에게 명백히 드러나도록 설계된 것들도 있지만(std::shared_ptr, std::unique_ptr) 보통 보이지 않도록 숨겨져 있다. 그렇기 때문에 보통 프록시 클래스는 auto와 잘 맞지 않는다. 때문에 다음과 같은 코드는 미정의 동작을 유발한다. // Widget을 하나 받고 std::vector을 돌려주는 다음과 같은 함수가 하나 있다고 하자. std::vector features(const Widget& w); // 해당 bool 값들..
[Effective Modern C++] 5. 명시적 타입 선언보다는 auto를 선호하자
[Effective Modern C++] 5. 명시적 타입 선언보다는 auto를 선호하자
2022.08.15아래처럼 auto는 반드시 초기화가 필요하다. int x1; // 문맥에 따라서는 초기화되지 않을 수 있음 auto x2; // 오류! 초기치가 꼭 필요함 auto x3 = 0; // 양호함: x3의 값이 잘 정의됨 여기서 반복자의 역참조를 통해 초기화되는 지역 변수 하나를 간단하게 선언한 아래의 코드를 보자. // b에서 e까지의 구간에 있는 모든 요소에 대해 dwim("do what I mean") 알고리즘을 수행한다. template void dwim(It b, It e) { for (; b != e; ++b) { typename std::iterator_traits::value_type currValue = *b; ... } } // auto를 사용하면 훨씬 간단하게(적은 타이핑으로) 선언할 수 ..