用于模板的限定和非限定名称查找的不同行为

时间:2011-12-14 08:18:36

标签: c++ templates lookup language-lawyer argument-dependent-lookup

此代码应如何运作?如果我在call_read()函数中使用限定名称,它会调用泛型函数忽略我的重载;如果我使用 unqualified 名称,它首先调用overload,然后调用泛型版本。有什么不同?这是海湾合作委员会的一个错误吗?

#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());
}

应该两次调用重载函数,但事实并非如此。 在这里查看结果 http://codepad.org/iFOOFD52

3 个答案:

答案 0 :(得分:9)

您所观察到的是两阶段名称查找参数相关查找的叠加。

让我们看看标准是什么(C ++ 03)。 [temp.dep]:

  

[...]表达形式:

postfix-expression ( expression-listopt )
     

其中postfix-expression是标识符,当且仅当表达式列表中的任何表达式是依赖于类型的表达式时,标识符表示从属名称(14.6.2.2)。

这意味着在read::read中,read是一个从属名称,因为x与类型有关。这意味着它在实例化时得到了解决。让我们看看这个[temp.dep.candidate]的规则是什么:

  

对于依赖于模板参数的函数调用,如果函数名称是unqualified-id但不是template-id,则使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了的是:

     

- 对于使用非限定名称查找(3.4.1)的查找部分,只能找到模板定义上下文中带有外部链接的函数声明。

因此,对于::read情况,仅考虑在模板定义之前声明的函数。但是:

  

- 对于使用关联命名空间(3.4.2)进行查找的部分,仅在模板定义上下文或模板<中找到具有外部链接的函数声明找到strong>实例化上下文。

对于非限定read这两个函数都被考虑,在模板定义和模板实例化时可见。

答案 1 :(得分:5)

是的,这是预期的行为。在第一种情况下(:: read),您有效地禁用ADL(依赖于参数的查找),这会将名称查找限制为在使用read之前已在全局范围中声明的内容。如果删除:: ADL将踢出可能会解析为功能模板之后声明的函数。

编辑:因为对于intdouble等基本类型没有ADL,这就解释了你的第二次观察。

答案 2 :(得分:-1)

编译器将始终调用与您的调用最匹配的方法。在这里你打电话:

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

因此,编译器会优先选择重载,因为它与调用完全匹配。它在模板特化的逻辑中,允许说如果存在一个更符合你的调用的重载函数,那么将使用这个。

对于问题的第二部分,关于使用完全限定名称和非限定名称的区别,它来自完全限定名称不依赖于任何其他内容的事实,因此解决了第一个匹配(这里你的模板声明)。