这个问题的灵感来自this one。考虑一下代码:
Class[] c = new Class[1];
c[0] = List.class;
XposedHelpers.callStaticMethod(XposedHelpers.findClass("com.android.internal.widget.LockPatternUtils", lpparam.classLoader), "patternToString", c, pattern);
经过GCC的一些测试后,我发现namespace ns {
template <typename T>
void swap(T& a, T& b) {
using namespace std;
swap(a, b);
}
}
解析为了
1)swap(a, b);
如果std::swap
超载T
(例如,标准容器类型)
2)std::swap
否则,导致无限递归
因此,似乎编译器将首先尝试在命名空间ns::swap
中找到匹配项。如果找到匹配项,则搜索结束。但是当ADL进入时并非如此,在这种情况下,无论如何都会找到ns
。解决过程似乎很复杂。
我想知道在上面的上下文中解析函数调用std::swap
的过程中发生了什么的细节。可以参考该标准。
答案 0 :(得分:6)
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-directives 具有非常特殊的行为C ++ 14 [namespace.udir] p2:
using-directive 指定指定命名空间中的名称 可以在 using-directive 之后出现的范围内使用 using-directive 。在非限定名称查找期间,名称 看起来好像是在最近的封闭命名空间中声明的 它包含 using-directive 和指定的命名空间。
包含名称空间std
和函数ns::swap
的块作用域的最近的封闭名称空间是全局名称空间。
另一方面,诸如using std::swap;
之类的使用声明确实将名称引入它们出现的范围,而不是在某些封闭范围内。
查找函数调用表达式(如swap(a, b)
)称为非限定查找。标识符swap
尚未使用任何名称空间或类名限定,而ns::swap
则已通过ns::
限定。对函数的潜在名称进行非限定查找包括两部分:纯粹的非限定查找和依赖于参数的查找。
纯不合格的查找将在包含该名称的最近的封闭范围处停止。在OP的示例中,如上面显示的等效转换所示,包含名称swap
的声明的最近范围是命名空间ns
。不会搜索全局范围,不会通过纯粹的非限定查找找到std::swap
。
依赖于参数的查找搜索与参数类型关联的所有范围(此处:仅命名空间和类)。对于类类型,声明类的名称空间是关联的作用域。 C ++标准库的类型(例如std::vector<int>
)与命名空间std
相关联,因此std::swap
可以通过依赖于参数的查找找到swap(a, b)
T
swap
是C ++标准库类型。类似地,您自己的类类型允许在声明它们的名称空间中找到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);
}
的通用版本通常如下实现:
var formatMoney = function (value) {
// Convert the value to a floating point number in case it arrives as a string.
var numeric = parseFloat(value);
// Specify the local currency.
return numeric.toLocaleString('USD', { style: 'currency', currency: "USD", minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
这需要一个移动构造加上两个移动分配,甚至可能会回复到副本。因此,您可以在与这些类型关联的名称空间中为您自己的类型提供专用交换函数。您的专业版可以使用您自己的类型的某些属性或私有访问权。
答案 1 :(得分:2)
标准中最重要的部分是7.3.4 / 2(引用C ++ 14 n4140,强调我的):
using-directive 指定指定命名空间中的名称可以在范围内使用 using-directive 出现在 using-directive之后。 在非限定名称查找(3.4.1)期间,名称出现 好像它们是在最近的封闭命名空间中声明的,它包含 using-directive 和 提名命名空间。
using-directive 位于:: ns
的函数内,并指定:: std
。这意味着,出于非限定名称查找的目的,此 using-directive 的效果是::std
中的名称就像在::
中声明它们一样。特别是,不就像它们在::ns
中一样。
因为非限定名称查找在::ns
中的函数内开始,所以它会在查看::ns
之前搜索::
。它会找到::ns::swap
,因此它会在不检查::
的情况下结束,它会在 using-directive中找到::std::swap
。