web-dev-qa-db-ja.com

クラス内で定義されたフレンド関数の完全修飾名は何ですか?

クラス内で定義されたフレンド関数の完全修飾名は何ですか?

最近、次のような例を見ました。以下のval()の完全修飾名は何ですか?

_#include <iostream>

namespace foo {
    class A {
        int x;
    public:
        A(int x = 0) : x(x) { }

        friend int val(const A &a) { return a.x; }
    };
}

int main() {
    foo::A a(42);

    // val() found using ADL:
    std::cout << val(a) << std::endl;

    // foo::val(a); // error: 'val' is not a member of 'foo'
    // foo::A::val(a); // error: 'val' is not a member of 'foo::A'

    return 0;   
}
_

val()が見つかる唯一の方法は、引数依存のルックアップですか?

確かに、これは実際的な問題に起因するものではありません。私は単により良い理解を得るために見ています。

46
Szabolcs

Val()を見つける唯一の方法は、引数依存のルックアップですか?

はい、それが唯一の方法です。 [namespace.memdef]/ で聖なる標準を引用するには:

非ローカルクラスのフレンド宣言が最初にクラス、関数、クラステンプレート、または関数テンプレートを宣言する場合、フレンドは最も内側にある名前空間のメンバーです。フレンド宣言は、それ自体では、非修飾ルックアップまたは修飾ルックアップに対して名前を表示しません。

したがって、valfooのメンバーですが、friend宣言だけからルックアップすることはできません。クラス外の定義(宣言でもあります)は、それを可視化するために必要です。インライン定義(クラス外宣言なし)の場合、ADLが関数を呼び出す唯一の方法であることを意味します。


追加のボーナスとして、C++にはかつて「フレンド名インジェクション」という概念がありました。ただし、これは削除され、ADLのルールは代替として調整されました。より詳細な概要は、WG21のペーパー N0777 (pdf)にあります。

36
StoryTeller

C++標準[7.3.1.2/3(ISO/IEC 14882:2011)]:

名前空間で最初に宣言されたすべての名前は、その名前空間のメンバーです。 非ローカルクラスのフレンド宣言がクラスまたは関数を最初に宣言する場合、フレンドクラスまたは関数は最も内側の名前空間のメンバーです。フレンドの名前はその名前空間スコープで一致する宣言が提供されるまで、非修飾ルックアップ(3.4.1)または修飾ルックアップ(3.4.3)(クラス定義がフレンドシップを付与する前または後。フレンド関数が呼び出された場合、名前は、関数の引数の型に関連付けられた名前空間およびクラスからの関数を考慮する名前ルックアップによって見つけることができます(3.4.2)。 friend宣言の名前が修飾もテンプレートIDでもなく、宣言が関数またはelaborated-type-specifierの場合、エンティティが以前に宣言されているかどうかを判断するためのルックアップ外部のスコープを考慮しない最も内側の名前空間

7
msc