본문 바로가기

컴퓨터과학

[Effective C++] (15-4) 가능하면 항상 constexpr을 사용할 것 / (16-1) const 멤버 함수를 스레드에 안전하게 작성할 것 class Point { public: ... constexpr void setX(double newX) noexcept // C++14 { x = newX; } constexpr void setY(double newY) noexcept // C++14 { y = newY; } ... }; 이를 이용해서 이제 이런 함수를 작성할 수 있습니다. // 원점을 기준으로 p와 대칭인 Point 객체를 돌려준다 (C+=14) constexpr Point reflection(const Point& p) noexcept { Point result; // 비const Point를 생성 result.setX(-p.xValue()); // 그 Point의 x와 y를 설정 result.setY(-p.yValue()); re.. 더보기
[Effective C++] (15-3) 가능하면 항상 constexpr을 사용할 것 생성자와 적절한 멤버 함수들이 constexpr인 사용자 형식도 리터럴 형식이 될 수 있습니다. class Point { public: constexpr Point(double xVal = 0, double yVal = 0) noexcept : x(xVal), y(yVal) {} constexpr double xValue() const noexcept { return x; } constexpr double yValue() const noexcept { return y; } void setX(double newX) noexcept { x = newX; } void setY(double newY) noexcept { y = newY; } private: double x, y; }; 여기서 Point의 생성자.. 더보기
[Effective C++] (15-2) 가능하면 항상 constexpr을 사용할 것 여러 가지 방식으로 실행할 수 있는 어떤 실험의 결과를 담는 자료구조가 필요하다고 합시다. 예를 들어 실험 과정에서 조명의 밝기를 높게 설정하거나, 낮게 설정하거나, 아예 끌 수도 있습니다. 또한 선풍기 속도나 온도도 높음, 낮음, 끔으로 설정할 수 있습니다. 실험에 관련된 환경 조건이 n개이고 각 조건이 가질 수 있는 상태가 세 가지라고 한다면, 가능한 조합은 총 3의 n승 가지입니다. 따라서 조건들의 모든 조합에 대한 실험 결과를 저장하려면 적어도 3의 n승개의 값을 담을 수 있는 자료구조가 필요합니다. 각 실험 결과가 하나의 int 값이고 n이 컴파일 도중에 알려진다고(또는 계산할 수 있다고) 가정할 때, 그러한 자료구조로는 std::array가 적당할 것입니다. 그런데 컴파일 도중에 3의 n승을 .. 더보기
[Effective C++] (15-1) 가능하면 항상 constexpr을 사용할 것 C++11에서 가장 헷갈리는 단어를 뽑는다면 아마 constexpr이 승자가 될 것입니다. constexpr을 객체에 적용했을 때에는 본질적으로 const의 강화된 버전처럼 작용하지만, 함수에 적용했을 때에는 상당히 다른 의미로 작용합니다. 독자가 표현하고자 하는 것이 constexpr에 해당하는 경우에는 constexpr을 적용하는 것이 아주 바람직하므로, constexpr에 관해 헷갈리는 점들을 명확히 밝히는 것은 가치 있는 일입니다. 개념적으로, constexpr은 어떠한 값이 단지 상수일 뿐만 아니라 컴파일 시점에서 알려진다는 점을 나타냅니다. 그러나 개념만 이해하는 것으로는 부족합니다. constexpr을 함수에 적용할 때에는 상황이 생각보다 좀 더 미묘해지기 때문입니다. 놀라운 결말을 미리 누.. 더보기
[Effective C++] (14-4) 예외를 방출하지 않을 함수는 noexcept로 선언할 것 앞에서 어떤 함수들은 noexcept가 자연스러운 구현이라고 말했음을 주목하기 바랍니다. 함수를 noexcept로 선언하기 위해 하마수의 구현을 작위적으로 비트는 것은 마치 꼬리가 개를 흔드는 격이자, 마차를 말 앞에 두는 격이자, 나무만 보고 숲을 보지 않는 격이다. 싱거운 비유는 그만하고, 함수의 직접적인 구현이 예외를 던질 수 있다고 할 때(이를테면 예외를 던질 수 있는 함수를 호출하기 때문에), 그 사실을 호출자에게 숨기기 위해 구현을 억지로 고치면(이를테면 모든 예외를 잡고 그것들을 상태 부호(status code)나 특별한 반환 값으로 대체해서) 함수의 구현이 복잡해질 뿐만 아니라 호출 지점의 코드도 복잡해질 가능성이 큽니다. 예를 들어 호출자는 상태 부호나 특별한 반환 값을 점검해야 합니다... 더보기
[Effective C++] (14-3) 예외를 방출하지 않을 함수는 noexcept로 선언할 것 noexcept가 특히나 바람직한 또 다른 예로 swap 함수들이 있습니다. swap은 여러 STL 알고리즘 구현에서 핵심 구성 요소이며, 복사 배정 연산자들에서도 흔히 쓰입니다. 그처럼 여러 곳에서 쓰이기 때문에, noexcept를 통해서 최적화할 가치가 큽니다. 흥미롭게도, 표준 라이브러리에 있는 swap들의 noexcept 여부는 사용자 정의 swap들의 noexcept 여부에 어느 정도 의존합니다. 예를 들어 다음은 표준 라이브러리에 있는 배열에 대한 swap과 std::pair에 대한 swap의 선언들입니다. template void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a, *b))); // 본문 참고 template struct pair .. 더보기
[Effective C++] (14-2) 예외를 방출하지 않을 함수는 noexcept로 선언할 것 * C++ noexcept 예외 명세가 "throw()"인 함수에는 그러한 최적화 유연성이 없으며, 예외 명세가 아예 없는 함수 역시 마찬가지로 그런 유연성이 없습니다. 다음은 이상의 여러 상황을 요약한 것입니다. 반환형식 함수이름(매개변수목록) noexcept; // 최적화 여지가 가장 크다 반환형식 함수이름(매개변수목록) throw(); // 최적화 여지가 더 작다 반환형식 함수이름(매개변수목록) // 최적화 여지가 더 작다 예외를 산출하지 않는 함수를 noexcept로 선언하는 것이 좋은 이유는 이 예만으로 충분할 것입니다. 그런데 더욱 강력한 이유가 있는 함수들도 있습니다. 두드러진 예는 이동 연산들입니다. std::vector을 사용하는 어떤 C++98 코드 기반에서, 종종 Widget들을 pu.. 더보기
[Effective C++] (14-1) 예외를 방출하지 않을 함수는 noexcept로 선언할 것 C++98에서 예외 명세(exception specification)는 다소 변덕스러운 야수였습니다. 프로그래머는 함수가 방출할(emit) 수 있는 예외 형식들을 요약해야 했으며, 그래서 함수의 구현을 수정하면 예외 명세도 바꾸어야 할 가능성이 생겼습니다. (* 예외가 함수 바깥으로 전파되는 것을 말합니다. 예외의 '발생' 또는 '던지기'와는 구분되는 개념입니다. 예외가 발생하지 않는다면 예외가 방출되는 일도 없지만, 예외가 발생한다고 해서 반드시 방출되는 것은 아닙니다.) 그런데 예외 명세가 변하면 클라이언트 코드가 깨질 수 있습니다. 호출자가 원래의 예외 명세에 의존할 수도 있기 때문입니다. 대체로 컴파일러는 함수 구현과 예외 명세, 그리고 클라이언트 코드 사이의 일관성 유지에 아무런 도움도 주지 않.. 더보기