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

ilo*_*oka 14 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);
    }

}
Run Code Online (Sandbox Code Playgroud)

如上所述,代码无法在Visual Studio 2008/2010上编译.错误是:

'void N::swap(N::C<T> &,N::C<T> &)' : could not deduce template argument for 'N::C<T> &' from 'int'.
Run Code Online (Sandbox Code Playgroud)

但是,如果我注释掉(1)并取消注释(2),它将编译好.是什么区别using namespace std,并using std::swap解释了这种行为?

Dav*_*eas 24

第一种情况是using指令(using namespace X),它的含义是命名空间中的名称X可用于常规查找,在第一个公共名称空间X和当前作用域中.在这种情况下,第一公共的命名空间的祖先::N::std::的,所以使用指令将使得std::swap只有当查找命中可用::.

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

另一方面,using declaration(using std::swap)提供当前上下文中实体的声明(在本例中是函数本身).查找将std::swap立即找到并停止定期查找::std::swap并将使用它.


Jam*_*nze 8

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

using指令指定名称将显示为"好像"它们是在最近的命名空间中声明的,该命名空间包含指令和指定的命名空间; 在您的情况下,全局命名空间.它实际上没有引入名称; 它只会影响名称查找.在依赖符号的情况下(或者总是在VC++的情况下)发生在呼叫站点.

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