using namespace无效

来源:互联网 发布:安卓录屏软件破解版 编辑:程序博客网 时间:2024/06/05 06:27

一、using namespace无效
C++ namespace与name lookup之惑


问题由 清风雨 于2005年底在 “namespace 和 操作符重载” 一文中提出,
讨论众多,但疑惑未解。


翻到此文时觉得不可思议,可事实如此,试了多个编译器都是相同结果。
试图去解释,可是过后细想还是有问题。
经查阅相关概念,大胆猜想并编码求证,终于有了一个合理解释,希望与大家共同研究。


原文中,在test命名空间重载了一个小于操作符("<"),并使用

    using namespace test;
   
进行使用。

可是出乎意料,std::sort()使用该"<"操作符时编译出错!

代码如下:

#include <vector>
#include <algorithm>

class testClass
{
};

namespace test
{
    bool operator<( const testClass &class1,
                    const testClass &class2 )
    {
        return true;
    }
};

using namespace test;

main()
{
    std::vector< testClass > vec;
    std::sort( vec.begin(),vec.end() );    // Fail!
}


对该代码请注意以下几点:

* 编译错误显示为:no match for 'operator<'.

* operator< 与类testClass不在一个命名空间。
如定义于同一命名空间或都是全局的则可以通过。

* 若类定义于namespace, 而操作符是全局的,也可以。

* operator< 是可见并可用的,如

using namespace test;

main()
{
    testClass obj1,obj2;
    bool b = obj1 < obj2;    // OK
}

* 即使std::sort()调用在test空间同,也是不行。

namespace test
{
    void test_sort( void )
    {
        std::vector< testClass > vec;
        std::sort( vec.begin(),vec.end() );    // Fail!
    }
};


仿佛“using namespace test;”并没有起效?

引用清风雨的原话:"我只是觉得,编译器应该在可见空间里,找到operator<。
而且直接使用也是可以的,只是用了std::sort就不可以了,这个感觉就有些怪怪的了。"

 

 

二、using namespace无效
C++ namespace与name lookup之惑
 
问题是否出在std::sort()里面?为了让问题更清晰,我们把std::sort()原码提出来。
将std::sort()中相关代码提出,生成std::my_sort().

#include <vector>
#include <algorithm>

class testClass
{
};

namespace test
{
    bool operator<( const testClass &class1,
                    const testClass &class2 )
    {
        return true;
    }
};

using namespace test;

namespace std
{
    template<typename _RandomAccessIterator>
    void mysort(_RandomAccessIterator __first,
                _RandomAccessIterator __last)
    {
        if (*__first < *__last)
            ;
    }
}

main()
{
    std::vector<testClass> vec;
    mysort(vec.begin(), vec.end());
}

正如所预料的,问题就在小于操作符的使用上。
再简化一点,去除iterator。如下代码问题依旧。

#include <vector>
#include <algorithm>

class testClass
{
};

namespace test
{
    bool operator<( const testClass &class1,
                    const testClass &class2 )
    {
        return true;
    }
};

using namespace test;

namespace std
{
    void testCompare(const testClass & t1,
                     const testClass & t2)
    {
        if (t1 < t2)
            ;
    }
}

main()
{
    testClass t1, t2;
    std::testCompare(t1, t2);
}

但是把testCompare()的命名空间改掉,如std001,竟然可以通过了!
testCompare()的namespace是std或是std001会有什么差别呢?
难道编译器对std库有特殊处理?不会。
可能是std namespace中有些声明造成了影响。

好,我们现在不需要std库了,把两个#include删去,
但是保持std::testCompare(),果然通过了。
再include一个stl头文件,又是错误!

 

三、using namespace无效!

C++ namespace与name lookup之惑


std空间中已定义的operator<造成了这个错误,这是以上测试得出的猜想。
stl里面pair定义最简单,stl_pair.h也不包含其它头文件,其中就有一个operator<,
所以 #include <bits/stl_pair.h> 试试,确实有问题。

再试试自定义一个operator<,让问题彻底清晰化。

class testClass
{
};

namespace test
{
    bool operator<( const testClass &class1,
                    const testClass &class2 )
    {
        return true;
    }
};

using namespace test;

namespace std001    // or std
{
    struct my_pair
    {
    };

    bool operator<(const my_pair & x,
                   const my_pair & y)
    {
        return true;
    }

    void testCompare(const testClass & t1,
                     const testClass & t2)
    {
        if (t1 < t2)    // Fail: no match '<'
            ;
    }
}

main()
{
    testClass t1, t2;
    std::testCompare(t1, t2);
}

这样就可以清楚地看到问题:

In function `void std001::testCompare(const testClass&, const testClass&)':
no match for 'operator<' in 't1 < t2'
candidates are: bool std001::operator<(const std001::my_pair&, const std001::my_pair&) 

果然是"std001::operator<"造成了问题。
但是为什么会这样,为什么对"test::operator<"视而不见?
难道using namespace test;无效?
试试using test::operator<;是有效的,那么

using namespace test;            // error?
using test::operator <;          // OK!

两者不能等效?


为了解释这个问题,必须先了解C++名字查找的相关概念。
搜索“C++ name lookup”可以找到此概念,如“GotW#30”就是对此概念的解释与例子。
当然“GotW#30”没有提出上述的问题,也没有对上述问题的提示。

What is koenig lookup?
主要是解释“koenig lookup(ADL)”的,同时也解释了“Ordinary name lookup(OL)”.
简单地说,名字查找分为两部分,一是“koenig lookup”,
也称为argument-dependent name lookup(ADL),即根据参数查找,
二是“Ordinary name lookup”,从最近的作用域开始查找。
但请注意这段文字:OL terminates as soon as the name is found。
OL可能找到的是错误的。
这就是上述代码所表现出来的问题。

std001::testCompare会在其参数所在空间查找operator<, 但并没有定义。
如果operator<与类testClass定义于同一命名空间则可利用ADL正确找到定义。

std001::testCompare在最近的作用域找到了std001::operator<,结果是错误的。

为什么对test::operator<视而不见?是否using namespace test;无效?
不是无效,是名字查找过程退出的原因.

这个OL错误问题更早的在一篇文章"C++ Lookup Mysteries"由Sven Rosvall描述并解释,
他的代码例子稍复杂,不容易看到问题所在,但他的解释却充分详实的多。

using namespace test;            // error?
using test::operator <;          // OK!
两者等效吗?

确实不等效。
“using namespace test”  is using-Directive,
“using test::operator<” is using-Declaration.
“using test::operator<” means '<' is fully-qualified name.

 (完)

原创粉丝点击