请参阅以下代码:
#include <algorithm>
namespace N
{
template <typename T>
class C
{
public:
void SwapWith(C & c)
{
using namespace std; // (1)
//using std::swap; // (2)
swap(a, c.a);
}
private:
int a;
};
template <typename T>
void swap(C<T> & c1, C<T> & c2)
{
c1.SwapWith(c2);
}
}
namespace std
{
template<typename T> void swap(N::C<T> & c1, N::C<T> & c2)
{
c1.SwapWith(c2);
}
}
如上所述,代码无法在Visual Studio 2008/2010上编译。错误是:
'void N::swap(N::C<T> &,N::C<T> &)' : could not deduce template argument for 'N::C<T> &' from 'int'.
但是,如果我注释掉(1)并取消注释(2),它将编译好。解释此行为的using namespace std
和using std::swap
之间有什么区别?
答案 0 :(得分:22)
第一种情况是using指令(using namespace X
),它的含义是命名空间X
中的名称可用于{{1}的第一个公共命名空间中的常规查找和当前的范围。在这种情况下,X
和::N
的第一个公共命名空间祖先是::std
,因此只有在查找命中::
时,using指令才会使std::swap
可用。
这里的问题是,当查找开始时,它将查看函数内部,然后在类内部,然后在::
内部,它将在那里找到N
。由于检测到潜在的重载,因此常规查找不会继续到外部命名空间::N::swap
。因为::
是一个函数,编译器将执行ADL(依赖于参数的查找),但基本类型的关联命名空间集是空的,因此不会带来任何其他重载。此时查找完成,并开始重载解析。它会尝试将当前(单个)重载与调用匹配,并且无法找到从::N::swap
转换为参数int
的方法,并且您会收到错误。
另一方面,using声明(::N::C
)提供当前上下文中实体的声明(在本例中是函数本身)。查找会立即找到using std::swap
,并停止使用std::swap
定期查找并使用它。
答案 1 :(得分:8)
显而易见的原因是使用声明和使用
指令有不同的效果。使用声明
将名称立即引入当前范围,所以
using std::swap
将名称引入本地范围;
查找在此处停止,您找到的唯一符号是std::swap
。
此外,这在定义模板时发生,因此稍后
找不到名称空间std
中的声明。在下面的
一行,仅 swap
将被考虑
在<algorithm>
中定义,加上ADL添加的那些(因此,一个
在命名空间N
)中。 (但这对VC ++来说是否正确?编译器
没有正确实现名称查找,所以谁知道。)
using指令指定名称将显示为“好像” 它们是在最近的命名空间中声明的,包含了两个 指令和指定的命名空间;在你的情况下,全球 命名空间。它实际上没有引入名称;它 只是影响名称查找。在受抚养人的情况下 符号(或在VC ++的情况下始终是)在调用时发生 站点。
至于为什么你有这个特殊的错误信息:可能更多 VC ++的一个问题,因为肯定没有不可推断的 你的代码中的上下文。但是没有理由期待这两者 无论编译器如何,变体都具有相同的行为。
答案 2 :(得分:2)
注意:我已在命名空间std中删除了交换定义。这与此无关。即使没有它,代码也会遇到同样的问题。
这是因为查找using directive
(using namespace std
)与using
declaration
(using std::swap
)之间的规则差异
如果是局部变量 与命名空间变量同名,命名空间变量是 隐。具有相同名称的命名空间变量是错误的 作为全球变量。
#include<iostream>
namespace T {
void flunk(int) { std::cout << "T";}
}
namespace V {
void flunk(int) { std::cout << "V";}
}
int main() {
using T::flunk; // makes T::flunk local
// using V::flunk; // makes V::flunk local. This will be an error
using namespace V; // V::flunk will be hidden
flunk(1);
}
根据这个,由于你的
template <typename T>
void swap(C<T> & c1, C<T> & c2)
使用时, std::swap
将被隐藏
using namespace std;
因此,唯一可用于模板扣除的swap
为N::swap
,它不适用于int
,因为它需要template class
作为参数。
但
时using std::swap;
在这种情况下,它等同于本地定义。并且可以用于解决问题。