混淆函数调用的解析 [英] Confusion around function call resolution

查看:160
本文介绍了混淆函数调用的解析的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题的灵感来自 this one < a>。考虑代码:

This question is inspired by this one. Consider the code:

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 无论如何找到。解决过程似乎很复杂。

After some test with GCC, I found that swap(a, b); resolves to
1) std::swap if T has overloaded std::swap (e.g., standard container types)
2) ns::swap otherwise, leading to infinite recursion.
So, it seems that the compiler will first try to find a match in the namespace ns. If a match is found, the search ends. But this is not the case when ADL comes in, in which case, std::swap is found anyway. The resolution process seems to be complicated.

我想知道在解决函数调用 swap(a,b)。

I want to know the details of what is going on under the hood in the process of resolving the function call swap(a, b) in the above context. Reference to the standard would be appreciated.

推荐答案

OP中的代码等效于

The code in the OP is equivalent to this:

using std::swap; // only for name lookup inside ns::swap

namespace ns {
  template <typename T>
  void swap(T& a, T& b) {
    swap(a, b);
  }
}

为什么?因为使用命名空间std; 使用指令有一个非常特殊的行为C ++ 14 [namespace.udir] p2:

Why? Because using-directives like using namespace std; have a very peculiar behaviour C++14 [namespace.udir]p2:


使用指令指定在指定的命名空间
中的名称可以在 using-directive 出现在
之后的 using-directive 。在非限定名称查找期间,名称
出现,就像它们在包含 using-directive 和指定命名空间的最近的包围命名空间
中声明。

A using-directive specifies that the names in the nominated namespace can be used in the scope in which the using-directive appears after the using-directive. During unqualified name lookup, the names appear as if they were declared in the nearest enclosing namespace which contains both the using-directive and the nominated namespace.

最近的包含命名空间 std 和函数的块范围 ns :: swap 是全局命名空间。

The nearest enclosing namespace that contains both namespace std and the block scope of function ns::swap is the global namespace.

使用声明

查找诸如 swap(a,b)之类的函数调用表达式称为未限定查询 。标识符 swap 未经任何命名空间或类名称限定,而不是 ns :: swap 已通过 ns :: 限定。对函数的潜在名称的无限制查找包括两个部分:纯无限查找和依赖查询的查找。

The lookup of a function call expression such as swap(a, b) is called unqualified lookup. The identifier swap has not been qualified with any namespace or class name, as opposed to ns::swap, which has been qualified via ns::. Unqualified lookup for potential names of functions consists of two parts: pure unqualified lookup and argument-dependent lookup.

纯非限定查找停止在包含名称的最近的包围范围。在OP的示例中,如上所示的等效变换所示,包含名为 swap 的声明的最近范围是命名空间 ns 。将不搜索全局范围, std :: swap 将不会通过纯无条件查找找到。

Pure unqualified lookup stops at the nearest enclosing scope that contains the name. In the OP's example, as illustrated by the equivalent transformation shown above, the nearest scope that contains a declaration of the name swap is the namespace ns. The global scope will not be searched, std::swap will not be found via pure unqualified lookup.

参数 - 依赖查找搜索与参数类型相关联的所有作用域(这里:仅命名空间和类)。对于类类型,已在其中声明类的命名空间是关联的范围。 C ++标准库的类型如 std :: vector< int> 与命名空间 std 相关联,因此 std :: swap 可以通过参数依赖查找表达式 swap(a,b)如果 T 是一种C ++标准库类型。类似地,您自己的类类型允许在它们声明的命名空间中找到 swap 函数:

Argument-dependent lookup searches all scopes (here: only namespaces and classes) associated with the argument types. For class types, the namespace in which the class has been declared in is an associated scope. Types of the C++ Standard Library such as std::vector<int> are associated with namespace std, hence std::swap can be found via argument-dependent lookup for the expression swap(a, b) if T is a C++ Standard Library type. Similarly, your own class types allow finding a swap function in the namespaces they have been declared in:

namespace N2 {
    class MyClass {};
    void swap(MyClass&, MyClass&);
}

因此,如果参数相关的查找找不到比纯不合格查找,您将最终调用 ns :: swap

Therefore, if argument-dependent lookup does not find a better match than pure unqualified lookup, you'll end up calling ns::swap recursively.

调用 swap 不合格,即交换(a,b)而不是 std :: swap(a,b)是通过参数相关查找找到的函数被假定为比 std :: swap 。为自己的类模板类型专门化一个函数模板例如 std :: swap 是不可能的(因为部分函数模板专门化是禁止的),并且您不能向命名空间添加自定义重载 std std :: swap 的通用版本通常如下实现:

The idea behind calling swap unqualified, that is, swap(a, b) instead of std::swap(a, b) is that functions found via argument-dependent lookup are assumed to be more specialized than std::swap. Specializing a function template such as std::swap for your own class template type is impossible (since partial function template specializations are forbidden), and you may not add custom overloads to namespace std. The generic version of std::swap is implemented typically as follows:

template<typename T>
void swap(T& a, T& b)
{
    T tmp( move(a) );
    a = move(b);
    b = move(tmp);
}

这需要移动构造加两个移动分配,回到副本。因此,您可以在与这些类​​型相关联的命名空间中为您自己的类型提供专用交换函数。您的专用版本可以使用您自己类型的某些属性或私有访问权限。

This requires a move-construction plus two move-assignments, which might even fall back to copies. Therefore, you can provide a specialized swap function for your own types in the namespaces associated with those types. Your specialized version can make use of certain properties of, or private access to, your own types.

这篇关于混淆函数调用的解析的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆