在C ++中使用指令与使用声明交换

时间:2013-04-22 17:08:35

标签: c++ swap using-directives using-declaration

请参阅以下代码:

#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 stdusing std::swap之间有什么区别?

3 个答案:

答案 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 directiveusing namespace std)与using declaration using std::swap)之间的规则差异

Microsoft

  
    

如果是局部变量     与命名空间变量同名,命名空间变量是     隐。具有相同名称的命名空间变量是错误的     作为全球变量。

  
#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; 

因此,唯一可用于模板扣除的swapN::swap,它不适用于int,因为它需要template class作为参数。

using std::swap;

在这种情况下,它等同于本地定义。并且可以用于解决问题。