14

このコードはどのように動作する必要がありますか?関数で修飾名を使用すると、オーバーロードを無視してジェネリック関数を呼び出しcall_read()ます。修飾されていない名前を使用すると、最初にオーバーロードが呼び出され、次に汎用バージョンが呼び出されます。違いは何ですか?GCCのバグですか?

#include <iostream>

struct info1 {};
struct info2 {};

template<class T> void read(T& x)
{
   std::cout << "generic" << std::endl;
}

template<class T> void call_read(T& x)
{
   ::read(x); // if I replace ::read(x) with read(x) the overload is called
}

void read(info1& x)
{
   std::cout << "overload" << std::endl;
}

int main()
{
   info1 x;
   info2 y;
   call_read(x);
   call_read(y);
}

また、基本的なタイプでは動作が異なることに気づきました。以下のコードを参照してください

#include <iostream>

typedef struct info1 {};
typedef struct info2 {};
typedef int info3;
typedef double info4;

template<class T> void read(T x)
{
    std::cout << "generic" << std::endl;
}

template<class T> void call_read(T x)
{
    read(x);
}

void read(info1 x)
{
    std::cout << "overload" << std::endl;
}
void read(info3 x)
{
    std::cout << "overload" << std::endl;
}

int main()
{
    call_read(info1());
    call_read(info2());
    call_read(info3());
    call_read(info4());
}

オーバーロードされた関数を2回呼び出すことになっていますが、そうではありません。ここで結果を参照して くださいhttp://codepad.org/iFOOFD52

4

3 に答える 3

9

あなたが観察しているのは、2 フェーズの名前のルックアップ引数依存のルックアップの重ね合わせです。

標準の内容を見てみましょう (C++03)。[温度差]:

[...]次の形式の式で:

postfix-expression ( expression-listopt )

ここで、postfix-expression は識別子であり、expression-list 内のいずれかの式が型依存式 (14.6.2.2) である場合にのみ、識別子は依存名を示します。

つまり、 と の両方read::read、は型read依存であるため、依存名ですx。つまり、インスタンス化の時点で解決されます。この [temp.dep.candidate] のルールを見てみましょう。

テンプレート パラメーターに依存する関数呼び出しの場合、関数名が非修飾 ID であるがテンプレート ID ではない場合、候補関数は通常の検索規則 (3.4.1、3.4.2) を使用して検索されますが、次の点が異なります。

— 非修飾名ルックアップ (3.4.1) を使用したルックアップの部分では、テンプレート定義コンテキストからの外部リンケージを持つ関数宣言のみが検出されます。

したがって、この::read場合、テンプレート定義の前に宣言された関数のみが考慮されます。しかし:

—関連付けられた名前空間(3.4.2)を使用したルックアップの部分では、テンプレート定義コンテキストまたはテンプレートインスタンス化コンテキストのいずれかで見つかった外部リンケージを持つ関数宣言のみが検出されます。

非修飾readの両方の関数が考慮され、それらはテンプレート定義とテンプレートのインスタンス化で表示されます。

于 2011-12-14T08:30:09.037 に答える
5

Yes, this is the expected behaviour. In the first case (::read) you effectivly disable ADL (argument dependent lookup) which restricts name lookup to things that have been declared in the global scope before your use of read. If you remove :: ADL will kick which may resolve to functions you declared after your function template.

Edit: And since for fundamental types like int and double there is no ADL, this explains your 2nd observation.

于 2011-12-14T08:33:02.557 に答える
-1

コンパイラは、呼び出しに最も一致するメソッドを常に呼び出します。ここでは、次のように呼び出します。

read(T& x) [with T = info1]

したがって、コンパイラは呼び出しと正確に一致するため、オーバーロードを優先します。テンプレートの特殊化のロジックでは、呼び出しにより適したオーバーロードされた関数が存在する場合、これが使用されると言うことができます。

完全修飾名と非修飾名を使用する場合の違いに関する質問の 2 番目の部分については、完全修飾名が他に依存していないため、最初の一致に解決されるという事実に由来します (ここではテンプレート宣言)。 )。

于 2011-12-14T08:31:42.730 に答える