传递右值作为引用

Passing rvalue as reference

本文关键字:引用      更新时间:2023-10-16

我有一些从svn存储库下载的Qt代码。自从我研究它以来已经有一段时间了,但我确信它曾经编译过。

有一个新版本的Qt和编译器(与我上次拥有的一样(。我目前的编译器是:mingw 4.9.2 32 位。

所以这是我的问题代码:

QByteArray dataBlock = audioTestFile.read(PACKET_SIZE_TO_ENCODE);
// This line is the issue
uint8Vect_t testVect = encodeData(uint8Vect_t(dataBlock.begin(), dataBlock.end()));

哪里:

typedef std::vector<uint8_t> uint8Vect_t;

uint8Vect_t encodeData(uint8Vect_t &dataBuff);

所以你可以在这里看到我有一个函数 encodeData(( 它接受一个参数uint8Vect_t &(通过 ref 传递(。我正在从QByteArray dataBlock迭代器(我已经测试过(中传递一个使用 std::vector 构造函数(其中一个需要两个迭代器(创建的临时变量(我认为是右值(。

但是,我收到错误:

../audioTest/txaudiostream.cpp:在成员函数中 'void CTxAudioStream::p layFile((': ../audioTest/txaudiostream.cpp:212:94: 错误:调用没有匹配函数 'CTxAudioStream::encodeData(uint8Vect_t(' uint8Vect_t testVect = encodeData(uint8Vect_t(dataBlock.begin((, dataBlock.end((((; ^ ../audioTest/txaudiostream.cpp:212:94:注意:候选人是: ../audioTest/txaudiostream.cpp:36:13:注意:uint8Vect_t CTxAudioStream::encodeData(uint8Vect_t&( uint8Vect_t CTxAudioStream::encodeData(uint8Vect_t &dataBuff( ^ ../audioTest/txaudiostream.cpp:36:13:注意:参数 1 没有已知的从 'uint8Vect_t {aka std::vector}' 到 'uint8Vect_t& {aka std::vector&}' 的转换

基本上是说我无法从uint8Vect_t转换为uint8Vect_t&。但是,如果我将 uint8Vect_t 类型的变量传递到函数中(而不是构造器/temp 变量的返回值(,那么这就可以了。

我以为在 c++11 中你可以传递右值......但我显然在这里遗漏了一些东西。谁能解释一下:

  1. 为什么这是错误的?
  2. 什么是高效/优雅(可读(的解决方案?

你的问题是

uint8Vect_t encodeData(uint8Vect_t &dataBuff);

在这里,您正在引用uint8Vect_t. 这适用于普通变量,但uint8Vect_t(dataBlock.begin(), dataBlock.end())是一个临时对象,不能绑定到左值引用。

如果encodeData()没有改变dataBuff那么最简单的解决方案是采用可以绑定到临时const &

uint8Vect_t encodeData(const uint8Vect_t &dataBuff);

如果必须更改dataBuff的内容,则必须编写另一个采用右值引用的encodeData()版本

uint8Vect_t encodeData(uint8Vect_t &&dataBuff);

这将允许函数绑定到临时向量,您可以在函数中像处理普通向量一样处理它。


我相信你看到这个的原因是你的旧编译器是Microsoft Visual Studio的一个版本。 MSVS 具有默认启用的非标准扩展,允许临时对象绑定到左值引用。 您可以在以下位置阅读有关它的更多信息:绑定到临时的 Visual Studio 错误的非常量引用?


添加此项是为了向您展示如何更改encodeData()以获取右值引用,而无需编写新函数。

#include <iostream>
#include <vector>
std::vector<int> modify(std::vector<int>& foo)
{
    for (auto & e : foo)
        e *= 2;
    return foo;
}
std::vector<int> modify(std::vector<int>&& foo)
{
    return modify(foo);
}

int main()
{
    std::vector<int> foo = modify({ 1,2,3,4,5 });
    for (const auto & e : foo)
        std::cout << e << " ";
}

现场示例

在上面的例子中,modify({ 1,2,3,4,5 })调用modify(std::vector<int>&& foo),然后在函数中调用foo是一个lvaue。 然后,我们返回将"new"左值传递给modify(std::vector<int>& foo)的结果,然后返回修改后的向量。

当您使用

encodeData(uint8Vect_t(dataBlock.begin(), dataBlock.end()))

传递给函数的向量是临时对象,引用不能绑定到临时对象。

如果函数不修改参数,则简单的解决方案是使其成为对常量对象的引用:

uint8Vect_t encodeData(uint8Vect_t const& dataBuff);

对常量对象的引用可以绑定到临时对象。

你想对你传递的对象做什么?

当你把它当作uint8Vect_t &dataBuff时,这应该意味着你想对它进行持久的修改,如果它是暂时的,这是没有意义的。

当你把它当作uint8Vect_t const&dataBuff时,这应该意味着你想从中复制而不是修改它,这可能是你想要的

当你把它当作uint8Vect_t dataBuff时,这应该意味着你需要你自己的本地临时副本,随心所欲地使用,然后扔掉,这应该足够重要,值得复制的成本。

当您将其视为uint8Vect_t &&dataBuff时,这应该意味着您希望从临时对象中进行非持久的修改(例如内容窃取(,调用者实际上承诺在您完成处理后将其丢弃。

最后一个选择是 C++11 中用于传递右值的新选择。

任何函数的返回值都是临时对象(rvalue(,不能将临时对象作为引用传递。

下面的代码将生成与我们尝试传递"saurabh"(temp 对象(作为引用类型相同的错误。

void fun(string& name){
  //statements;
}
int main(){
    fun("Saurabh");
    return 0;
}