2007年01月02日

友達宣言の仕方――名前空間内のクラスから名前空間外のモノをfriend宣言

オブジェクト指向の概念を取り入れたC++にはfriendという構文が在る。他のクラスや関数から非publicなメンバの利用を許可する『親友をつくる』機能だ。例えば次のコードはエラーとならない。


class A {
    // Bクラスをフレンドクラス宣言
    friend class B;

    // gf_getAm関数をフレンド関数宣言
    friend int gf_getAm(A &a);

private:
    int m_;
};

class B {
    int getAm(A &a) {
        // BクラスからAクラスのprivateメンバにアクセス出来る.
        return a.m_;
    }
};

int gf_getAm(A &a)
{
    // gf_getAm関数からAクラスのprivateメンバにアクセス出来る.
    return a.m_;
}

今回はそんなfriendに関する話。

名前空間外へfriend

例によって例のごとく、C++でプログラミング中の私、天井冴太。ある名前空間内のクラスでグローバルスコープのクラスをフレンドクラスに宣言したい。なので次のように書いた。


namespace ns {
    class C1 {
        friend class C2;
        int i;

    public:
        C1(int a) : i(a) {}
    };
} // namespace ns;

class C2 {
public:
    void set(ns::C1 &c1, int a) { c1.i = a; }
    int  get(ns::C1 &c1) { return c1.i; }
};

Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.
test.cpp
test.cpp(13) : error C2248: 'ns::C1::i' : private メンバ (クラス 'ns::C1' で宣言されている) にアクセスできません。
        test.cpp(4) : 'ns::C1::i' の宣言を確認してください。
        test.cpp(2) : 'ns::C1' の宣言を確認してください。
test.cpp(14) : error C2248: 'ns::C1::i' : private メンバ (クラス 'ns::C1' で宣言されている) にアクセスできません。
        test.cpp(4) : 'ns::C1::i' の宣言を確認してください。
        test.cpp(2) : 'ns::C1' の宣言を確認してください。

test.cpp: In member function `void C2::set(ns::C1&, int)':
test.cpp:4: error: `int ns::C1::i' is private
test.cpp:13: error: within this context
test.cpp: In member function `int C2::get(ns::C1&)':
test.cpp:4: error: `int ns::C1::i' is private
test.cpp:14: error: within this context

あれ?変だな?……名前空間が悪さしてるのかな?friend class C2;::C2では無くns::C2を探しに行ってしまっているとか?

という訳で、friend class C2;friend class ::C2;に変更してみる。

コンパイル……よし、VC++ 2005のコンパイラは通った。GCCはどうだ?


test.cpp:3: error: expected type-name
test.cpp:3: error: friend declaration does not name a class or function
test.cpp: In member function `void C2::set(ns::C1&, int)':
test.cpp:4: error: `int ns::C1::i' is private
test.cpp:13: error: within this context
test.cpp: In member function `int C2::get(ns::C1&)':
test.cpp:4: error: `int ns::C1::i' is private
test.cpp:14: error: within this context

うーん、エラーが取れない。むしろ増えてるんですけど……


test.cpp:3: error: expected type-name
test.cpp:3: error: friend declaration does not name a class or function

……の部分がアヤシイ気がする。小一時間悩んでコードの先頭にclass C2;を追加してみる。……ま、無理だろうけど…………通ったよ

つまるトコ最終的に以下のようなコードに。


class C2;
namespace ns {
    class C1 {
        friend class C2;
        int i;

    public:
        C1(int a) : i(a) {}
    };
} // namespace ns;

class C2 {
public:
    void set(ns::C1 &c1, int a) { c1.i = a; }
    int  get(ns::C1 &c1) { return c1.i; }
};

……って、ええぇぇぇ。1行目にclass C2;が必要なんて、チョットどうよ。美しくないぞ。friend宣言部分だけで何とかならんのか?

関数だと?

ココでふと疑問。クラスはこれで上手くいったが、じゃあ関数の場合は?

試しにC1::iにアクセスするグローバル関数を作成。上記コードclass C2;の部分をそのプロトタイプにしてみた。


int f();
namespace ns {
class C1 {
    friend int f();
    int i;

public:
    C1(int a) : i(a) {}
};
} // namespace ns;

int f()
{
    ns::C1 c(1);
    return c.i;
}

Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

test.cpp
test.cpp(23) : error C2248: 'ns::C1::i' : private メンバ (クラス 'ns::C1' で宣言されている) にアクセスできません。
        test.cpp(7) : 'ns::C1::i' の宣言を確認してください。
        test.cpp(4) : 'ns::C1' の宣言を確認してください。

あれー、通らないfriendが認識されてない。

チョット考えてfriend int f();friend int ::f();に変更してみた。……よし通った。

……って、クラスだと::無くて良くて関数だと必要ってどうよ。統一感無いなぁ。……もしかして規格上はどちらの場合でも::要るんだろうか?VC++GCCも規格外の動きをしているだけで。試しにクラスへのfriend宣言のコードfriend class C2;friend class ::C2;にしてもコンパイル通ったし。どうなんだろう。

JISでのfriend

ところで、今回このエントリを書くにあたってJISのC++規格書(JISのサイトからX3014で検索して出てくるヤツ)を覗いてみたが、JIS C++規格ではfriendの事を『随伴』と表現している事を初めて知った。……んー、『随伴』ねぇ……『friend』の単語から受けるイメージとズレるなぁ……

ラベル:C++
posted by 天井冴太 at 21:17| Comment(0) | TrackBack(0) | Study | 更新情報をチェックする
この記事へのコメント
コメントを書く
コチラをクリックしてください

この記事へのトラックバック
×

この広告は90日以上新しい記事の投稿がないブログに表示されております。