代码改变世界

一个有趣的问题:临时变量能否为l-value,或者,能否有non-const引用?

2010-12-20 22:47  Rollen Holt  阅读(1264)  评论(3编辑  收藏  举报

前几天碰到这个问题,考虑以下代码:

 

class bar{};
void foo(const bar&) {}
void foo(bar&) {}

void main()
{
foo(bar());
}
foo(bar())会调用哪个函数?
我分别在vc 6.0 sp3, vc.net 7.0和vc.net 7.1 final beta上作了试验,结果很有趣。
vc6和vc70的结果一样:在foo(const bar&)和foo(bar&)都存在时,编译器选择foo(bar&),并给出一个level 4警告:
"warning C4239: nonstandard extension used : 'argument' : conversion from 'class bar' to 'class bar &'
A reference that is not to 'const' cannot be bound to a non-lvalue"
如果注释掉foo(bar&),则编译器选择foo(const bar&)且没有任何警告。
在vc7.1下,如果两者都存在,编译器选择foo(const bar&);如果注释掉foo(const bar&),则选择foo(bar&)并给出上面的警告。
msdn对warning C4239的解释是:
"nonstandard extension used : 'token' : conversion from 'type' to 'type'
This type conversion is not allowed by the C++ draft standard, but it is permitted here as an extension.
This warning is always followed by at least one line of explanation describing the language rule being violated."
看起来C++标准草案认为non-lvalue不应该有non-const引用,至少认为对non-lvalue的non-const引用是不安全的。
换句话说,C++似乎认为临时变量是non-lvalue。
再来看这行代码:
bar() = bar();
这行代码在三个vc版本中都能编译通过,没有任何警告。这是否说明临时变量可以是l-value?
看起来,C++标准草案虽然不允许,但microsoft的人认为临时变量为l-value是有一定合理性的,因此作了扩展。
但是在vc6和vc7.0中,在const引用和non-const引用都存在的情况下,编译器对临时变量却优先选择了non-const引用;vc7.1的选择似乎更合理些--他优先选择了non-const引用。

如果你也这么认为,那麻烦又来了。看看这句:
std::cout << reinterpret_cast<std::ostringstream&>( std::ostringstream() << "X" ).str();
猜猜cout会输出什么?在vc71中,输出的不是字符串"X",而是一个内存地址!
原因是编译器选择了成员操作符ostream& ostream::operator<<(const void*), 而不是非成员操作符ostream operator<<( ostream&, const char* )。
因为如果选择非成员操作符就意味着对临时变量std::ostringstream()产生一个non-const引用。结果就是,在vc71中,const char*被解释为const void*!
更让人疯狂的是这句:
std::ostringstream() << "X" << "X";
第一个调用成员操作符ostream::operator<<(const void*),第二个调用非成员操作符operator<<(ostream&,const char*)
这就是我前几天在一个项目中碰到的情况:vc6和vc70中正常工作的代码,用vc71编译后本来应该输出字符串的地方却输出了内存地址。
当然解决方法也是有的,比如,可以这么写:
const_cast<std::ostringstream&>( std::ostringstream() ) << "X";
我目前的观点是,vc71的编译器的选择应该是正确的,但是stream library的实现有问题,不知道大家对这个问题怎么看。
如果认为stream library有问题的话,麻烦就大了,我看了一下stlport 4.5.3,发现也有这个问题。欢迎大家讨论。
另外,我目前手头没有其他编译器,不知道其他编译器是怎么处理这个问题的。