3

以下から始めます ( を使用gcc version 4.0.1):

namespace name {
   template <typename T>
   void foo(const T& t) {
      bar(t);
   }

   template <typename T>
   void bar(const T& t) {
      baz(t);
   }

   void baz(int) {
      std::cout << "baz(int)\n";
   }
}

(グローバル名前空間に)追加すると

struct test {};
void bar(const test&) {
   std::cout << "bar(const test&)\n";
}

すると、思った通り、

name::foo(test()); // produces "bar(const test&)"

しかし、私が追加するだけなら

void bar(const double&) {
   std::cout << "bar(const double&)\n";
}

このオーバーロードが見つからないようです:

name::foo(5.0) // produces "baz(int)"

そのうえ、

typedef std::vector<int> Vec;
void bar(const Vec&) {
   std::cout << "bar(const Vec&)\n";
}

どちらも表示されないので、

name::foo(Vec());

コンパイラエラーを与える

error: cannot convert ‘const std::vector<int, std::allocator<int> >’ to ‘int’ for argument ‘1’ to ‘void name::baz(int)’

これはルックアップがどのように機能するはずですか?(注: namespace を削除するnameと、すべてが期待どおりに機能します。)

barオーバーロードが考慮されるように、この例をどのように変更できますか? (オーバーロードはテンプレートのに考慮されるべきだと思いましたか?)

4

6 に答える 6

8

doubleグローバル名前空間にもバージョンを追加し、fooすべてが定義された後に main から呼び出すと仮定します。したがって、これは基本的に 2 フェーズの名前検索です。呼び出しの引数が (その型に) 依存しているために依存している非修飾関数名の検索は、2 つのフェーズで行われます。

最初のフェーズでは、定義コンテキストで非修飾の引数依存ルックアップを行います。次に、結果を凍結し、インスタンス化コンテキスト (インスタンス化の時点での宣言の合計) を使用して、2 番目の引数に依存するルックアップのみを実行します。修飾ルックアップはもう行われません。したがって、あなたの例では、次のことを意味します。

  • bar(t)内部の呼び出しは、インスタンス化コンテキストで引数依存ルックfoo<test>アップを使用してルックアップします (バー テンプレートが宣言されているbarため、非修飾ルックアップを使用しては見つかりません)。テンプレートの前または後にグローバルを定義するかどうかに応じて、最初のフェーズで既に引数依存ルックアップを使用してグローバル宣言を見つけます (これはの名前空間で定義されています)。次に、メインの呼び出しがインスタンス化され、このフェーズで見つかる可能性があります(テンプレートを宣言した後に宣言した場合)。fooabovebarfoobartestfoo<test>bar

  • bar(t)内の呼び出しは、基本型foo<int>であるため、引数依存のルックアップを行いません (または、ルックアップの結果は空の宣言セットになります) 。intしたがって、一致するbarテンプレートがテンプレートとして宣言されafterているため、定義コンテキストでの非修飾ルックアップでも何も見つかりませんfoo。呼び出しは不適切な形式であり、標準ではこの状況について次のように述べています。14.6.4.2/1

    呼び出しの形式が正しくない場合 [...]、プログラムの動作は未定義です。

    したがって、これを「私は汚いことをし、コンパイラは私を平手打ちしないことを選択した」場合と見なす必要があります:)

  • bar(t)withinの呼び出しfoo<Vec>は再びルックアップを行い、bar in を探しますstd::(そこにstd::vectorが定義されているため)。barそこにも、定義コンテキストにもありません。そのため、未定義の動作を再度bar実行することを決定し、テンプレートを使用します。テンプレート自体は、bazその後に宣言されたものを使用して未定義の動作を行い、ADL でも定義コンテキストからの非修飾ルックアップでも見つけることができません。

    ベクトルが の場合、 のvector<test>ルックアップbarもグローバル スコープで実行されます。これは、引数に依存するルックアップが引数の型を直接使用するだけでなく、テンプレート引数が存在する場合はそれらの型も使用するためです。


GCC を使用する場合は、その動作に完全に依存しないでください。次のコードでは、呼び出しがあいまいであると主張していますが、コードはまったく問題ありません。 finafakeは候補にすべきではありません。

namespace aname {
  struct A { };
  void f(A) { }
}

namespace afake {
  template<typename T>
  void g(T t) { f(t); }
  void f(aname::A) { }
}

int main() { aname::A a; afake::g(a); }

スニペットの適合性をテストする場合は、厳格な設定でcomeau オンライン コンパイラを使用することをお勧めします。

于 2009-09-09T21:50:13.600 に答える
2

あなたが私のシステムで見ている動作を確認でき、それは正しいと信じています。

barオーバーロードの解決はその引数の名前空間を調べているだけのように見えるので、そのバージョンはグローバル名前空間にあるため動作し、コンパイラはそのバージョンをチェックtestします。あなたが正しく指摘したように、テンプレート版。testbar

バージョンのVec重要な名前空間はstd. barのバージョンを入れると、stdそれがピックアップされることがわかります。

グローバル名前空間は組み込みタイプであり、グローバル名前空間に特別に関連付けられていdoubleないため、ルックアップに使用されないため、バージョンは機能しません。double

于 2009-09-08T22:14:28.643 に答える
2

「c++ koenig lookup」でグーグルを実行します

これで、テンプレート ルックアップ ルールに関する十分な情報が得られるはずです。

Herb Sutter には、このテーマに関する優れた記事があります:
http://www.gotw.ca/gotw/030.htm

于 2009-09-08T23:02:28.450 に答える
0

名前を検索するためのルールは、名前が修飾されていない場合、パラメーターの名前空間を使用して関数を検索することです。

name::foo(test());基本的にfoo呼び出しがbar(test());あり、テストの名前空間がバーの検索に使用されるため、機能します。この場合、グローバル名前空間。

name::foo(Vec());Vec はクラスや構造体ではなく typedef であるため、これは機能しません。

関数名のルックアップ規則については、C++ 標準を参照してください。

于 2009-09-09T02:28:12.213 に答える
-1

次のコードは、VS 2005 Professional Edition を使用して問題なくコンパイルできます。

#include <iostream>
#include <vector>

using std::cout;


typedef std::vector<int> Vec;

namespace name {
    template <typename T>
    void foo(const T& t) {
        bar(t);
    }

    template <typename T>
    void bar(const T& t) {
        baz(t);
    }

    void baz(int) {
        std::cout << "baz(int)\n";
    }   

    void bar(const Vec&) {
        std::cout << "bar(const Vec&)\n";
    }
}


int main()
{
    name::foo(Vec());
    return 0;
}

元のコードを投稿して、何が問題なのかを調べてください。

于 2009-09-08T22:11:55.840 に答える
-1

次のプログラムは、gcc 4.3 と gcc 4.1 で問題なく動作します (手元にあるコンパイラは 2 つだけです。

#include <iostream>
#include <vector>

namespace name {
   template <typename T>
   void foo(const T& t) {
      bar(t);
   }

   template <typename T>
   void bar(const T& t) {
      baz(t);
   }

   void baz(int) {
      std::cout << "baz(int)\n";
   }

   struct test {};
    void bar(const test&) {
       std::cout << "bar(const test&)\n";
    }

    void bar(const double&) {
       std::cout << "bar(const double&)\n";
    }

    typedef std::vector<int> Vec;
    void bar(const Vec&) {
       std::cout << "bar(const Vec&)\n";
    }
}

int main()
{
    name::foo(name::test());
    name::foo(5.0);
    name::foo(name::Vec());
}

生産:

bar(const test&)
bar(const double&)
bar(const Vec&)

どのコンパイラを使用していますか?

于 2009-09-08T22:07:48.750 に答える