18

请参考以下代码:

#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),它将编译 OK。using namespace stdusing std::swap解释这种行为的区别是什么?

4

3 回答 3

32

第一种情况是 using 指令 ​​( using namespace X),这意味着命名空间中的名称X将可用于常规查找,在X当前范围的第一个公共命名空间中。::N在这种情况下, and的第一个公共命名空间祖先::std::,因此 using 指令std::swap仅在查找命中时才可用::

这里的问题是,当查找开始时,它会在函数内部查找,然后在类内部查找,然后在内部N查找::N::swap。由于检测到潜在的过载,因此常规查找不会继续到外部命名空间::。因为::N::swap是一个函数,编译器将执行 ADL(依赖于参数的查找),但基本类型的关联命名空间集是空的,因此不会带来任何其他重载。此时查找完成,重载决议开始。它将尝试将当前(单个)重载与调用相匹配,并且它将无法找到从参数转换int为参数的方法,::N::C并且您会收到错误消息。

另一方面, using 声明 ( using std::swap) 在当前上下文中提供实体的声明(在这种情况下,在函数本身内部)。查找将std::swap立即找到并停止常规查找::std::swap并将使用它。

于 2013-04-22T18:19:33.763 回答
9

显而易见的原因是 using 声明和 using 指令具有不同的效果。using 声明将名称立即引入当前范围,因此 using std::swap将名称引入本地范围;查找在此处停止,您找到的唯一符号是std::swap. 此外,这发生在定义模板时,因此std找不到命名空间中的后续声明。在下一行中,唯一 swap会考虑的是在 中定义的<algorithm>,加上由 ADL 添加的(因此,在 namespace 中的那个N)。(但是 VC++ 是这样吗?编译器没有正确实现名称查找,所以谁知道。)

using 指令指定名称将“好像”在包含指令和指定命名空间的最近命名空间中声明;在你的情况下,全局命名空间。它实际上并没有介绍名称;它只会影响名称查找。在依赖符号的情况下(或者总是在 VC++ 的情况下)发生在调用站点。

至于为什么你有这个特定的错误消息:可能更多的是 VC++ 的问题,因为你的代码中肯定没有不可推导的上下文。但是,无论编译器如何,都没有理由期望这两个变体具有相同的行为。

于 2013-04-22T18:17:07.687 回答
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,它不适用于ints,因为它需要 atemplate class作为参数。

不是什么时候

using std::swap;

在这种情况下,它等同于local definition。并且可以毫无问题地使用。

于 2013-04-22T17:24:10.347 回答