一、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.
(完)