C++0x:右值引用与非常量左值
在 C++03 中编程时,我们无法将未命名的临时 T()
传递给函数 void foo(T&);
。通常的解决方案是给临时一个名称,然后像这样传递它:
T v;
foo(v);
现在,C++0x 出现了 - 现在有了右值引用,一个定义为 void foo(T&&)
将允许我通过临时。这让我想到了我的问题:由于采用右值引用的函数可以采用右值引用(未命名的临时变量)和左值引用(命名的非常量引用),那么还有什么理由在函数参数中再使用左值引用呢?我们不应该总是使用右值作为函数参数吗?
诚然,采用左值引用的函数会阻止调用者传递临时值,但我不确定这是否是一个有用的限制。
When programming in C++03, we can't pass an unnamed temporary T()
to a function void foo(T&);
. The usual solution is to give the temporary a name, and then pass it like:
T v;
foo(v);
Now, along comes C++0x - and now with rvalue references, a function defined as void foo(T&&)
will allow me to pass a temporary. Which brings me to my question: since a function that takes an rvalue reference can take both rvalue references (unnamed temporaries) as well as lvalue references (named non-const references), is there any reason to use lvalue references anymore in function parameters? Shouldn't we always use rvalues as function parameters?
Granted, a function that takes an lvalue reference would prevent the caller from passing a temporary, but I'm not sure if that's a useful restriction.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
“因为接受右值引用的函数可以接受右值引用(未命名的临时对象)以及左值引用(命名的非常量引用)”
这是一个不正确的陈述。在右值参考规范的第一次迭代期间,这是正确的,但它不再是,并且至少在 MSVC 中实现了,以符合后来的更改。换句话说,这是非法的:
为了调用带有左值的右值引用的函数,您必须将其转换为右值,如下所示:
当然,该语法非常清楚采用左值引用的函数与采用左值引用的函数之间的区别采用右值引用的实际情况是:右值引用预计不会在调用中幸存。这是一件大事。
现在,您当然可以编写一个新函数来执行 std::move 的功能,然后您“可以”使用类似于左值引用的右值引用。我考虑过使用我拥有的访问者框架来执行此操作,有时您根本不关心访问者调用的任何结果,但有时您会关心访问者调用的任何结果,因此在这些情况下需要左值引用。通过右值引用,我可以同时获得两者......但这违反了右值引用语义,因此我认为这是一个坏主意。
基于此,您的声明可能会造成混乱:
这有效,但不是因为您将左值作为右值引用传递。它之所以起作用是因为引用衰减(也是 C++0x 中的新功能)。当您将左值传递给这样的模板时,它实际上会被实例化,如下所示:
引用衰减表示
&&&
变成&
所以实际的实例化看起来就像这样:换句话说,您只是通过引用传递左值...没有什么新的或特别的。
希望事情能澄清。
"since a function that takes an rvalue reference can take both rvalue references (unnamed temporaries) as well as lvalue references (named non-const references)"
This is an incorrect statement. During the first iterations of the rvalue reference specification this was true, but it no longer is and is implemented at least in MSVC to comply with this later change. In other words, this is illegal:
In order to call a function expecting rvalue references with an lvalue you must turn it into an rvalue like so:
Of course, that syntax makes it quite clear what the difference between a function taking an lvalue reference and one taking an rvalue reference really is: an rvalue reference is not expected to survive the call. This is a big deal.
Now, you can of course make up a new function that does exactly what std::move does and then you "can" use rvalue references sort of like lvalue references. I thought about doing this for instance with a visitor framework I have when sometimes you simply don't care about any result of the visitor call, but other times you do and thus need an lvalue reference in those cases. With an rvalue reference I could get both...but it's such a violation of the rvalue reference semantics that I decided it was a bad idea.
Your statement may be a confusion based upon this:
This works, but not because you are passing an lvalue as an rvalue reference. It works because of reference decay (also new in C++0x). When you pass an lvalue to such a template it actually gets instantiated like so:
Reference decay says that
&&&
turns into&
so then the actual instantiation looks like this:In other words, you're simply passing an lvalue by reference...nothing new or special about that.
Hope that clears things up.
如果必须主动释放临时内存(例如指向新内存的指针或文件句柄等有限资源),那么这是一个有用的限制。但需要传递这些信息更像是“糟糕的设计”,而不是“有用的限制”。
It's a useful restriction if that temporary must be actively disposed of, say a pointer to
new
memory or a limited resource like a file handle. But needing to pass those back smells more of "bad design" than it does of "useful restriction."