ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 항목11. 정의되지 않은 비공개 함수보다 삭제된 함수를 선호하라
    Effective Modern C++ 2022. 7. 25. 17:07

    코드의 특정 함수를 호출하지 못하게 하는 가장 흔한 방법은 선언을 하지 않는 것이다. 하지만 C++이 자동으로 함수를 선언하는 경우가 있으며, 그런 경우 클라이언트가 그 함수들을 호출하지 못하게 하기는 쉽지 않다.

    그런 함수들의 사용을 방지하기 위한 C++98의 접근방식은 private로 선언하고 정의하지 않는 것이다. 이러면 클라이언트는 이 함수을 호출할 수 없다. 이들에 접근할 수 있는 코드(멤버 함수나 클래스의 friend 함수)에서 호출한다고 해도 정의가 없어서 링크가 실패한다.
    C++11에서는 같은 목적을 달성하는 더 나은 방법이 있다. 이는 바로 선언의 끝에 "= delete" 를 붙이는 것이다. = delete를 붙인 함수를 삭제된 함수라고 부른다.

    public:
        basic_ios(const basic_ios&) = delete;    // 삭제된 함수

     

    삭제된 함수는 어떠한 방법(멤버 함수나 클래스의 friend 함수)으로도 접근할 수 없고, 부적절한 용례가 링크 시점에 가서야 발견되는 C++98의 방식에 비하여 적절하다. (삭제된 함수는 private이 아니라 public으로 선언하는 것이 관례)
    private 방식은 멤버 함수에만 사용할 수 있지만 삭제된 함수는 어떠한 함수도 삭제할 수 있다.
    암묵적 변환에 의해 아래의 코드는 논리에는 어긋나지만 무사히 컴파일된다.

    bool isLucky(int number);
    
    if(isLucky('a')) ...
    if(isLucky(true)) ...
    if(isLucky(3.5)) ...

    이는 논리적으로 어긋나는 것이라 위의 호출들이 아예 컴파일되지 않게 만드는 것이 바람직하다.

     

    다음과 같이 삭제된 함수로 지정한 매개변수 형식들은 암묵적 변환이 되지 않아 컴파일되지 않는다.

    bool isLucky(int number);
    bool isLucky(char) = delete;
    bool isLucky(bool) = delete;
    bool isLucky(double) = delete;

     

    삭제된 함수의 또 하나의 장점은 원치 않는 템플릿 인스턴스화를 방지하는 것이다.
    포인터에는 특별한 포인터 두 가지가 있다. 하나는 void* 포인터이다. 이 포인터는 역참조나 증가, 감소가 아예 불가능하다. 또 하나는 char* 포인터로, 개별 문자를 나타내는 것이 아니라 C 스타일 문자열을 나타낸다는 점에서 특별하다.
    그래서 이 두 포인터를 호출하지 못하게 하는 템플릿을 구현한다 해보자.

    template<>
    void processPointer<void>(void*) = delete;
    template<>
    void processPointer<const void>(const void*) = delete;
    template<>
    void processPointer<char>(char*) = delete;

    const volatile void*, const volatile char*, wchar_t, char16_t, char32_t의 포인터들에 대한 버전들도 삭제하면 좋다.


    하지만 private로 템플릿의 일부 인스턴스화를 방지하는 것은 불가능하다. 그 이유는 템플릿 특수화는 반드시 클래스 범위가 아니라 이름공간 범위에서 작성해야 한다는 것이다. 그래서 다른 접근 수준을 지정할 필요가 없는 삭제된 함수는 멤버 함수를 클래스 바깥에서 삭제하는 것이 가능하다.

    class Widget{
    public:
        ...
        template<typename T>
        void processPointer(T* ptr)
            {...}
        ...
    };
    template<>
    void Widget::processPointer<void>(void*) = delete;
Designed by Tistory.