web-dev-qa-db-ja.com

ADLは友達のインライン関数を呼び出す唯一の方法ですか?

fの宣言内で、Sのフレンド関数としてSを定義しましょう。

struct S
{
    friend void f() {}
};

fを呼び出す方法が見つかりません。

それでは、そのようなインラインフレンド関数は 引数に依存するルックアップでのみ呼び出すことができるというのは本当ですか?

struct S
{
    friend void f() {}
    friend void g(S const&) {}
} const s;

int main()
{
    // f();     // error: 'f' was not declared in this scope
    // S::f();  // error: 'f' is not a member of 'S'
    g(s);
    // S::g(s); // error: 'g' is not a member of 'S'
}

ボーナス:関数ポインタを取得したい場合はどうなりますか/ std::function/lambdaからg

24
YSC

では、そのようなインラインフレンド関数は、引数に依存するルックアップでのみ呼び出すことができるというのは本当ですか?

はい。 [namespace.memdef]/ で指定されているとおり:

非ローカルクラスのfriend宣言が最初にクラス、関数、クラステンプレート、または関数テンプレートを宣言する場合。友人は、最も内側を囲む名前空間のメンバーです。フレンド宣言自体は、名前を非修飾ルックアップ([basic.lookup.unqual])または修飾ルックアップ([basic.lookup.qual])に表示しません。

fonly宣言はインライン定義であるため、修飾または非修飾のルックアップからは見えません。ただし、ADLには、このようなフレンド関数用の特別な規定があります [basic.lookup.argdep]/4

関連する名前空間を検討する場合、ルックアップは、関連する名前空間が修飾子([namespace.qual])として使用されるときに実行されるルックアップと同じですが、次の点が異なります。

  • 関連するクラスで宣言された名前空間スコープのフレンド関数またはフレンド関数テンプレートは、通常のルックアップ([class.friend])中に表示されなくても、それぞれの名前空間内に表示されます。

あなたのボーナスの質問に関しては、ラムダはそれをするべきです:

auto exposed_g = [](S const& s){ g(s); };

ADLを本体にラップします。ただし、戻り値の型の控除に関する通常の警告が適用されます。これは値になります(voidを返さないと仮定します)。

名前ffriend宣言 で宣言されています。Sを含む namespace のメンバーになりますが、表示されません。名前空間スコープで再宣言されていない限り、名前検索用。そうでない場合は、 [〜#〜] adl [〜#〜] でのみ見つけることができます。

非ローカルクラスX内のフレンド宣言によって導入された名前は、Xの最も内側を囲む名前空間のメンバーになりますが、通常の名前ルックアップ(非修飾でも修飾でもない)では、一致する宣言は、クラス定義の前または後に、名前空間スコープで提供されます。このような名前は、名前空間とクラスの両方を考慮するADLを介して見つけることができます。

8
songyuanyao

いいえ。関数を正しく宣言する必要があります。

struct S;
inline void f();
inline void g(S const&);

struct S
{
    friend void f() {}
    friend void g(S const&) {}
} const s;

int main()
{
    f(); // Ok
    // S::f();  // error: 'f' is not a member of 'S'
    g(s);
    // S::g(s); // error: 'g' is not a member of 'S'
}

オンラインコンパイラ

2
user7860670