12

関数オブジェクトは、オーバーロードの解決中に通常の関数とは異なる方法で処理されますか? もしそうなら、どのように?

関数を同等に呼び出し可能な関数オブジェクトに置き換えると、コードの意味が変わる次のケースに遭遇しました。

#include <iostream>

namespace N
{
    enum E { A, B };

    void bar(E mode) { std::cout << "N::bar\n"; }
}

template <typename... Args>
void bar(Args&&... args) { std::cout << "global bar\n"; }

int main()
{
    bar(N::A);
}

ここでの出力は「N::bar」です。これまでのところ、N::bar は ADL によって検出されており、N::bar とグローバル バーの両方が完全に一致しており、N::bar はテンプレートではないため優先されています。

しかし、グローバル バーを関数オブジェクトに変更すると、次のようになります。

#include <iostream>

namespace N
{
    enum E { A, B };

    void bar(E mode) { std::cout << "N::bar\n"; }
}

struct S
{
    template <typename... Args>
    void operator()(Args&&... args) { std::cout << "global bar\n"; }
};
S bar;

int main()
{
    bar(N::A);
}

出力は「グローバル バー」になりました。違いはなぜですか?

4

2 に答える 2

12

ここで重要な点は、ADL は、名前関数呼び出しの関数であるとルックアップが判断した場合にのみ作動するということです。2 番目のケースでは、 は関数ではなくオブジェクトbarであることが判明したため、式は関数呼び出しではなく、オブジェクトへのの適用です。関数呼び出しではないため、ADL は機能せず、考慮されません。bar(N::A)operator()barN::bar

3.4.1/3

関数呼び出しの後置式として使用される非修飾名のルックアップは、3.4.2 で説明されています。[注: 式が関数呼び出しの後置式であるかどうかを (解析中に) 判断するために、通常の名前検索規則が適用されます。3.4.2 [ADL]のルールは、式の構文解釈には影響しません。

別の見方をすると、ADL はオーバーロードされた関数のセットに新しい関数を追加することに注意してください。ただし、2 番目の例ではそのようなセットはありません。ルックアップによってオブジェクトが検出され、オブジェクトのメンバーが呼び出されます。

于 2012-08-29T04:01:30.100 に答える
4

3.4.2p3 を参照してください。

X を非修飾ルックアップ (3.4.1) によって生成されたルックアップ セットとし、Y を引数依存ルックアップ (次のように定義) によって生成されたルックアップ セットとします。Xが含まれている場合

  • ...
  • 関数でも関数テンプレートでもない宣言

Y は空です。

そのような規則がない場合は、そのとおりです。ADL は他の関数をオーバーロード セットに追加します。実際、13.3.1.1p1 はこれに依存しています。2 つのブランチがあります。1 つはオペランドがクラス オブジェクトを表す関数呼び出し式用で、もう 1 つはオペランドが 1 つ以上の関数または関数テンプレートを表すものです。

于 2012-08-30T21:26:44.273 に答える