围绕函数调用解析的混淆
Confusion around function call resolution
这个问题的灵感来自这个问题。考虑代码:
namespace ns {
template <typename T>
void swap(T& a, T& b) {
using namespace std;
swap(a, b);
}
}
在使用 GCC 进行一些测试后,我发现swap(a, b);
解析为
1)std::swap
T
是否重载std::swap
(例如,标准容器类型)
2) 否则ns::swap
,导致无限递归。
因此,编译器似乎将首先尝试在命名空间ns
中找到匹配项。如果找到匹配项,则搜索结束。但当 ADL 进来时,情况并非如此,在这种情况下,无论如何都会找到std::swap
。解决过程似乎很复杂。
我想知道在上述上下文中解析函数调用swap(a, b)
的过程中幕后发生的事情的细节。如能提及该标准,将不胜感激。
OP 中的代码等效于以下内容:
using std::swap; // only for name lookup inside ns::swap
namespace ns {
template <typename T>
void swap(T& a, T& b) {
swap(a, b);
}
}
为什么?因为像using namespace std;
这样的using 指令有一个非常特殊的行为C++14 [namespace.udir]p2:
using-指令指定指定命名空间中的名称 可以在using-指令出现在之后的范围内使用使用指令。在非限定名称查找期间,名称 看起来好像它们是在最近的封闭命名空间中声明的 它包含using-指令和指定的命名空间。
包含命名空间std
和函数ns::swap
块作用域的最近的封闭命名空间是全局命名空间。
另一方面,诸如using std::swap;
之类的 using 声明确实将名称引入它们出现的范围,而不是在某个封闭范围内。
函数调用表达式(如swap(a, b)
)的查找称为非限定查找。标识符swap
没有用任何命名空间或类名限定,而ns::swap
则通过ns::
限定。对函数潜在名称的非限定查找由两部分组成:纯非限定查找和依赖于参数的查找。
纯非限定查找在包含名称的最近封闭作用域停止。在 OP 的示例中,如上面所示的等效转换所示,包含名称声明的最近的范围是命名空间swap
ns
。不会搜索全局范围,std::swap
不会通过纯非限定查找找到全局范围。
依赖于参数的查找搜索与参数类型关联的所有范围(此处:仅命名空间和类)。对于类类型,在其中声明类的命名空间是关联的作用域。C++标准库的类型(如std::vector<int>
)与命名空间std
相关联,因此std::swap
可以通过表达式的参数相关查找找到T
如果是C++标准库类型,则可以通过表达式swap(a, b)
的参数查找找到。同样,您自己的类类型允许在声明它们的命名空间中查找swap
函数:
namespace N2 {
class MyClass {};
void swap(MyClass&, MyClass&);
}
因此,如果依赖于参数的查找找不到比纯非限定查找更好的匹配项,则最终将以递归方式调用ns::swap
。
调用swap
非限定(即swap(a, b)
而不是std::swap(a, b)
)背后的想法是,通过依赖于参数的查找找到的函数被认为比std::swap
更专业。不可能将函数模板(如std::swap
)专用于您自己的类模板类型(因为禁止部分函数模板专用化),并且您不能将自定义重载添加到命名空间std
。std::swap
的通用版本通常按如下方式实现:
template<typename T>
void swap(T& a, T& b)
{
T tmp( move(a) );
a = move(b);
b = move(tmp);
}
这需要一个移动构造加上两个移动分配,甚至可能回退到副本。因此,可以在与这些类型关联的命名空间中为自己的类型提供专用的交换函数。专用版本可以使用您自己的类型的某些属性或专用访问权限。
标准中最重要的部分是 7.3.4/2(引用 C++14 n4140,强调我的):
using-指令指定指定命名空间中的名称可以在在非限定名称查找 (3.4.1) 期间,名称将显示 就好像它们是在最近的封闭命名空间中声明的,该命名空间同时包含using-指令和 指定的命名空间。
using-指令位于:: ns
函数内,并指定:: std
。这意味着,出于非限定名称查找的目的,此using-指令的效果是::std
中的名称的行为就像它们是在::
中声明的一样。特别是,不像他们在::ns
.因为非限定名查找始于::ns
函数内部,它会先搜索::ns
,然后再查找::
。它找到::ns::swap
,所以它到此结束,而不检查::
,在那里它会找到由 using-指令引入::std::swap
。
- 函数调用中参数的顺序重要吗
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 变量没有改变?通过向量的函数调用
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 我知道函数调用中存在歧义.有没有办法调用foo()函数
- 模板函数调用
- 获取从C++中同一类中的构造函数调用的方法返回的值
- 析构函数调用
- 成员函数调用和C++对象模型
- 使用共享指针的函数调用,其对象应为 const
- C++:编译时检查匹配的函数调用对?
- 函数调用C++中的参数太少
- 来自 DLL 的函数调用 [表观调用的括号前面的表达式必须具有(指向-)函数类型]
- 返回指向对象的指针的函数调用是否为 prvalue?
- C++ 如何重载 [] 运算符并进行函数调用
- 代码的效率. 转到和函数调用
- 是同一作用域的函数部分中的函数调用
- 如何封装一个函数,以便它只能由同一类中的一个其他函数调用?
- 类型擦除的std::function与虚拟函数调用的开销