传递右值作为参考

Passing rvalue as reference

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

我有一个新版本的 Qt 和编译器(我上次使用的)。我目前的编译器是:mingw 4.9.2 32-bit.

所以这是我的问题代码:

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 传递)。我正在传递一个临时变量(我认为是右值),它是使用 std::vector 构造函数(其中一个需要两个迭代器)从 QByteArray dataBlock 迭代器(我已经测试过的)创建的。

但是,我收到错误消息:

../audioTest/txaudiostream.cpp: In member function 'void CTxAudioStream::playFile()': ../audioTest/txaudiostream.cpp:212:94: error: no matching function for call to 'CTxAudioStream::encodeData(uint8Vect_t)' uint8Vect_t testVect = encodeData(uint8Vect_t(dataBlock.begin(), dataBlock.end())); ^ ../audioTest/txaudiostream.cpp:212:94: note: candidate is: ../audioTest/txaudiostream.cpp:36:13: note: uint8Vect_t CTxAudioStream::encodeData(uint8Vect_t&) uint8Vect_t CTxAudioStream::encodeData(uint8Vect_t &dataBuff) ^ ../audioTest/txaudiostream.cpp:36:13: note: no known conversion for argument 1 from 'uint8Vect_t {aka std::vector}' to 'uint8Vect_t& {aka std::vector&}'

基本上是说我无法从 uint8Vect_t 转换为 uint8Vect_t&。但是,如果我将 uint8Vect_t 类型的变量传递给函数(而不是构造函数/临时变量的 return 值),那么这就可以了。

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

  1. 为什么这是错误的?
  2. 什么是 efficient/elegant(可读)解决方案?

你想做什么with/to你传入的对象?

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

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

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

当你把它当作 uint8Vect_t &&dataBuff 时,这应该意味着你想从一个临时对象中进行非持久修改(例如内容窃取),调用者有效地承诺在你完成它后丢弃它.

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

当你使用

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

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

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

uint8Vect_t encodeData(uint8Vect_t const& dataBuff);

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

您的问题是

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 有一个默认情况下启用的非标准扩展,它允许临时对象绑定到左值引用。您可以在以下位置阅读更多相关信息:Non-const reference bound to temporary, Visual Studio bug?


添加这个是为了向您展示如何更改 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 << " ";
}

Live Example

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

Return 任何函数的值都是临时对象(右值),您不能将临时对象作为引用传递。

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

void fun(string& name){
  //statements;
}

int main(){
    fun("Saurabh");
    return 0;
}