13

最近、メンバー関数がクラス内で同じ名前のフリー関数を完全に隠していることに気付きました。そして完全に言うと、同じ名前のフリー関数はすべて、オーバーロード解決の対象とはまったく見なされないということです。なぜそれがこのようなもので行われるのか理解できます:

void f();

struct S
{
    void f();

    void g()
    {
        f(); // calls S::f instead of ::f
    }
};

関数が同一のシグネチャを持っている場合、変数のスコープが同じように機能するため、これは当然のことです。しかし、free 関数が次のように異なるシグネチャを持つ明確な呼び出しを禁止する理由は次のとおりです。

void f();

struct S
{
    void f(int x);

    void g()
    {
        f(); // fails to compile attempting to call S::f, which has wrong signature
    }
};

クラス内からシャドウされたフリー関数を呼び出す方法を尋ねているのではありません。私が知りたいのは、このデザインの背後にある理論的根拠です。

4

3 に答える 3

9

非修飾名ルックアップでは、一度に 1 つのスコープのみが考慮され、そのスコープでの検索で結果が得られない場合は、次に高いスコープが検索されます。あなたの場合、Sのスコープのみが検索されます。

しかし、free 関数が次のように異なるシグネチャを持つ明確な呼び出しを禁止する理由は次のとおりです。

問題は、名前のルックアップ自体が名前であるidentifier以外には関係がないことです。関数を呼び出したいという事実に完全に気付かず、識別子を見るだけです。を使用するだけで同じ名前のルックアップが発生しますauto x = f;。そのように考えると、非常に限られたスコープのみを検索する必要がある非常に正当な理由があります。それ以外は、ユーザーを驚かせるだけです。

于 2012-07-27T22:03:36.077 に答える
3

特別な、非常に驚​​くべきルールがあります (ただし、あなたの例には適用されません)。クラス メンバー名が名前検索によって見つかると、名前空間スコープは検索されません。

#include <string>

struct C {
    std::string s;

    explicit C (std::string);

    void swap (C& rhs) {
        swap (s, rhs.s); // error: swap is C::swap
    }   
};

void swap (C& lhs, C& rhs) {
    swap (lhs.s, rhs.s); // std::swap(string,string)
}

IMO、これはクレイジーです。

しかし、free 関数が次のように異なるシグネチャを持つ明確な呼び出しを禁止する理由は次のとおりです。

名前のルックアップは、解決をオーバーロードする前に行われます。

  • ルックアップがあいまいな場合、オーバーロードの解決は行われません。
  • 名前ルックアップで実行可能な関数が見つからない場合、他のラウンドのルックアップは試行されません。

オーバーロードと名前検索の間の「フィードバック」がなければ、ルールは十分に複雑です。複雑にするのではなく、単純化することをお勧めします (メンバーを非表示にする名前空間スコープ名のルールを削除したり、あいまいな名前のルックアップを削除したりするなど)。

于 2012-08-16T22:45:42.537 に答える
2

私は正式な回答を提供することはできません (Design and Evolution of C++当時の委員会からの引用を覚えているか、実際に委員会に参加していた人もいるかもしれません)。ある時点でどれだけのものがスコープ内にあるかを忘れがちです。さらに、オーバーロードの解決は非常に複雑になる可能性があり、デフォルトの引数と変換が存在する可能性があります。したがって、その場合、正確に何が呼び出されているかを常に確認するために、スコープを最も制限したいと思います。

于 2012-07-27T21:50:17.837 に答える