5

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

#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

出于某种原因,我希望使用fooinsidebar来找到它下面的重载。将 的重载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稍后在翻译单元中声明的重载 s?

4

1 回答 1

2

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

我敢肯定,就在您使用另一个参数的那一刻,运算符和函数案例没有区别。试试如果在第一个版本中还使用 struct Foo 添加另一个参数会发生什么...

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

在您的情况下,唯一的参数是 int,并且它没有关联的命名空间,因此再也找不到任何东西。在第二种情况下,您还可以使用 Foo 来拖动 ::,并且您的运算符位于 :: 中。

于 2013-07-02T19:10:37.420 回答