稍后定义的重载函数的名称查找

Name lookup for overloaded functions defined later

本文关键字:查找 函数 重载 定义      更新时间:2023-10-16

我注意到在依赖稍后定义的函数时,函数查找的奇怪行为:

#include <iostream>
template <typename T>
void foo(const T&)
{
    std::cout << "Basic" << std::endl;
}
template <typename T>
void bar()
{
    T x;
    foo(x);
}
void foo(const int& x)
{
    std::cout << "int:" << x << std::endl;
}
int main()
{
    bar<int>();
}

输出:

Basic

出于某种原因,我希望在bar内部使用foo来找到它下面的重载。将foo的重载移动到bar之上会使输出成为所需的int:0(或者只是写一个声明)。

这种相同的行为似乎不适用于重载二进制运算符:

#include <iostream>
struct Foo {} foo;
template <typename T>
void operator<<(const Foo&, const T&)
{
    std::cout << "Basic" << std::endl;
}
template <typename T>
void bar()
{
    T x;
    foo << x;
}
void operator<<(const Foo&, const int& x)
{
    std::cout << "int:" << x << std::endl;
}
int main()
{
    bar<int>();
}

输出:

int:0

我有两个问题,第一个问题是:为什么行为是这样的,为什么操作员过载不同?第二个问题是:如果我有一个命名函数(就像我使用foo一样),是否有一种方法可以编写函数bar,从而发现稍后在翻译单元中声明的过载foo

欢迎来到最著名的两相查找和怪异规则的世界。

我确信运算符和函数的情况没有区别,只是因为你又使用了一个参数。尝试一下,如果在第一个版本中还添加了另一个具有结构Foo的参数,会发生什么。。。

分两个阶段查找意味着在编译模板时,对于依赖名称,它会四处查找并记住可见函数集。在你的情况下,什么也找不到。然后在实例化上下文中有另一个查找,遵循ADL(参数相关查找)规则。仅此而已。这意味着首先要收集参数的"关联名称空间",然后在这些名称空间中寻找更多的候选者。

在您的情况下,唯一的参数是int,并且它没有关联的名称空间,因此不会再次找到任何内容。在第二种情况下,您还拥有拖动::的Foo,并且您的操作符位于:中。