web-dev-qa-db-ja.com

フレンド関数を静的として宣言することは可能ですか?

コンパイルして正常に動作するいくつかのC++サンプルコードを次に示します。

class A
{
public:
   A() {/* empty */}

private:
   friend void IncrementValue(A &);
   int value;
};

void IncrementValue(A & a)
{
   a.value++;
}   

int main(int, char **)
{
   A a;
   IncrementValue(a);
   return 0;
}

ただし、私がしたいのは、IncrementValue()を静的として宣言し、別のコンパイルユニットから表示または呼び出せないようにすることです。

static void IncrementValue(A & a)
{
    a.value++;
}

ただし、これを行うとコンパイルエラーが発生します。

temp.cpp: In function ‘void IncrementValue(A&)’:
temp.cpp:12: error: ‘void IncrementValue(A&)’ was declared ‘extern’ and later ‘static’
temp.cpp:8: error: previous declaration of ‘void IncrementValue(A&)’

...そして、friend宣言を一致するように変更しても役に立たない:

friend static void IncrementValue(A &);

...このエラーが発生するため:

temp.cpp:8: error: storage class specifiers invalid in friend function declarations

私の質問は、静的に宣言されている(メソッドではない)フレンド関数をC++で使用する方法はありますか?

28
Jeremy Friesner

引用N3691-§11.3/ 4 [class.friend]

フレンド宣言で最初に宣言された関数は、外部リンケージ(3.5)を持っています。それ以外の場合、関数は以前のリンケージ(7.1.1)を保持します。

したがって、関数をstaticとして宣言する必要がありますfriendとして宣言します。これを行うには、Aの定義の上に次の宣言を追加します。

class A;  // forward declaration, required for following declaration
static void IncrementValue(A&); // the friend declaration will retain static linkage
22
Praetorian

承知しました。エラーメッセージの2行目を注意深く読みます。関数はexternおよび後でstaticとして宣言されています。だからあなたがしなければならないすべては、friend宣言の前にそれを静的に宣言することです:

class A;
static void IncrementValue(A&);

class A {
    // class definition, including friend declaration
};

static void IncrementValue(A&) {
    // code here, of course
}
17
Pete Becker

プレトリアンの answer は、あなたが明示的に尋ねた質問に答えるという点で技術的に正しいですが、彼が提案することは不健全であり、希望するというあなたの述べた目的も満たさないという点で、有用な答えではないと思いますフレンドクラスの翻訳単位でのみ呼び出すことができるメソッドを定義します。

彼の解決策には2つの問題があります。最初に、静的関数宣言の前にあるクラス定義を含むヘッダーを含む他の変換ユニットは、静的に宣言されたフレンド関数が参照する変換モジュールで定義されていないというエラーにより、コンパイルに失敗します。 2番目に、参照TUは、静的に宣言された関数自体を定義することにより、そのコンパイルエラーを排除でき、その定義は、関数がフレンドと宣言されたクラスのすべてのプライベートデータにアクセスできます。これは、フレンド関数は常にデフォルトであるパブリックリンケージのままにしておく必要があることを示唆しています。これにより、コンパイルエラーであるパブリックリンケージ関数の複数の定義によるカプセル化違反の可能性が回避されます。

@engfがあなたの質問へのコメントで正しい方向に進んでいると思います。アクセスできるようにしたいクラスと同じ翻訳単位で定義されたフレンドクラスが必要です。例えば。

// A.h

class A
{
public:
   A() : _value(0) {}
private:
   int _value;
   friend struct A_Accessor;
};
// A.cpp

struct A_Accessor
{
   static void IncrementValue(A& a)
   {
      ++a._value;
   }
};


TEST(StaticInit, IncrementA)
{
   A a;
   A_Accessor::IncrementValue(a);
}

これは、Aのプライベートデータへのアクセスを許可する方法でIncrementValueを定義しますが、Aの変換モジュールの外部から参照することはできません。

10
Neutrino