C++0x 미리보기 6, 확장된 friend 선언

류광, 2008/04/01 19:51
friend가 좀 더 유용해집니다.
  • 제안의 명칭: Extended friend Declarations
  • 제안자: William M. Miller
  • 관련 문서: N1520, N1616, N1722, N1791

이 제안은 friend에 가해진 몇 가지 불필요한 제약들을 없애자는 것입니다. 현재 C++ 표준에서는 다음과 같은 friend 선언이 허용되지 않습니다.

class C;
typedef C Ct;

class X1 {
    friend C; // (1)
};

class X2 {
    friend Ct; // (2)
};

template <typename T> class R {
    friend T; // (3)
};

(1) 현재 C++에서 다른 클래스를 친구로 삼는 경우 반드시 class 키워드를 붙여서 friend class C;라고 해야 합니다. 그러나 이 경우 C가 클래스임을 이미 컴파일러도 알고 있으므로 class를 꼭 써줘야 하는 것은 불필요한 제약이라고 할 수 있습니다. struct나 union도 마찬가지입니다.

(2) 현재 C++에서는 typedef로 정의된 형식 이름을 friend 선언에 사용할 수 없습니다. 다른 대부분의 경우에는 원 클래스 이름과 typedef된 이름을 차별 없이 사용할 수 있다는 점에서 역시 불필요한 제약입니다.

(3) 현재 C++에서는 템플릿 인수를 친구로 삼을 수가 없습니다.

이러한 제약들이 생긴 이유는 N1520의 II. History에 나와 있는데, 아주 간단하게 이야기하면 다른 어떤 문제를 해결하기 위한 제약이 friend에까지 번졌던 것이라고 보면 될 것 같습니다. 어쨌든, 이 제안에 의해 위의 세 가지 제약을 모두 제거됩니다.

(1)에 대해 한 가지 부연 설명을 하자면, 이 제안이 적용된다고 해도 class 키워드를 반드시 붙여야 하는 경우가 여전히 남아 있습니다. 다음 예처럼 아직 존재하지 않은 클래스를 친구로 선언하는 경우입니다.

class C;
class X1 {
    friend C; // 기존 클래스를 친구로 선언
    friend class D; // 새로운 클래스를 선언함과 동시에
                    //이 클래스의 친구로 선언. 이
                   //  경우에는 class 키워드가 필수임.
};

(3)과 관련해서는, 템플릿 인수를 친구로 삼을 수 있으면 여러 가지 재미있는 기법이 가능해 집니다. 예를 들어 Java의 final 클래스처럼 상속이 불가능한 C++ 클래스를 만들고 싶다고 합시다. 현재 가능한 방법은 생성자가 private인 부모 클래스를 상속하고, 그 부모 클래스가 자식 클래스를 친구로 선언하는 것입니다. 다음은 Bjarne Stroustrup이 제시한 코드입니다(http://www.research.att.com/~bs/bs_faq2.html#no-derivation)

class Usable;

class Usable_lock {
    friend class Usable;
private:
    Usable_lock() {}
    Usable_lock(const Usable_lock&) {}
};

class Usable : public virtual Usable_lock {
    // ...
public:
    Usable();
    Usable(char*);
    // ...
};

Usable a;

class DD : public Usable { };

DD dd;  // 오류: DD::DD()에 접근할 수 없음
        // Usable과는 달리 DD는 Usable_lock의
        // 친구가 아니므로 Usable_lock::Usable_lock()에
        // 접근할 수 없다.

그런데 Usable_lock은 Usable만 알고 있으므로, 또 다른 상속 금지 클래스를 만들기 위해서는 Usable_lock과 본질적으로 동일한 부모 클래스를 만들거나 Usable_lock에 또 다른 friend 선언을 추가해야 합니다. 매번 그런 일을 하는 것은 비합리적이므로 당연히 템플릿을 떠올리게 됩니다.

template <class T>
class Non_derivable {
    friend T; // !! 현재는 불가능 !!
private:
    Non_derivable() {}
    Non_derivable(const Non_derivable&) {}
};

위의 템플릿 클래스가 컴파일된다면(즉 템플릿 친구 선언이 허용된다면) 다음 예와 같이 좀 더 쉽게 상속 금지 클래스들을 만들 수 있습니다.

class Foo : public Non_derivable<Foo> {
    // ...
};

class Bar : public Non_derivable<Bar> {
    // ... 마찬가지 ...
};

마지막으로 friend 자체에 대한 이야기 하나를 덧붙이자면, friend가 OOP의 캡슐화나 정보 은폐를 해친다는 주장이 있는데 그렇지 않습니다. 많은 경우 friend는 public 멤버의 개수를 줄여주며, 당연한 말이겠지만 public 멤버가 적을수록 캡슐화가 좋아집니다. friend를 피하기 위해 인위적으로 public 멤버를 추가한다면 그것이 더 해가 되지요.

top
트랙백 0 : 의견 # + 4

Trackback Address :: http://occamsrazr.net/tt/trackback/191

comments powered by Disqus

(2013년 11월 10일자로 블로그에도 DISQUS 시스템을 도입했습니다. 기존 의견의 수정, 삭제, 댓글 추가는 여전히 가능합니다.)

  1. yagur 2008/04/02 23:30 PERMALINKMODIFY/DELETE REPLY

    템플릿 인자가 friend가 되다니... 빨리 사용해 보고싶군요 =)

  2. 류광 2008/04/03 19:52 PERMALINKMODIFY/DELETE REPLY

    컴파일러 개발자들의 분투를 빌 뿐입니다 :)
    참고로 http://gcc.gnu.org/projects/cxx0x.html 에 g++의 c++0x 지원 현황이 나와 있습니다.
    Comeau 컴파일러는 이미 지원을 하고 있고요. 위의 예제들은http://www.comeaucomputing.com/tryitout/ 에서 시험해 보았습니다.



  3. cppig1995 2008/07/30 10:47 PERMALINKMODIFY/DELETE REPLY

    고품질 번역서들 잘 읽고 있습니다. 이번에 AI Programming Wisdom 2를 샀어요.
    Comeau라면 export를 당시 유일하게 지원했다는 컴파일러인가요? (설마 지금도 유일하진 않겠죠?)
    항상 표준의 반영이 빠른 것 같던데...

  4. 류광 2008/07/30 20:55 PERMALINKMODIFY/DELETE REPLY

    안녕하세요~ 시리즈가 계속 번역되지 못해서 안타깝습니다.

    현재 export를 지원하는 컴파일러로는 Comeau 외에 Intel 컴파일러가 있는데요... Comeau와 동일한 EDG 백엔드를 사용하는 제품이라서 export의 지원 상황이 많이 나아졌다고 하긴 좀 그렇네요. 언제 export를 둘러싼 이야기를 한 번 써보겠습니다....