传递右值作为参考
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 中你可以传递右值..但我显然在这里遗漏了一些东西。谁能解释一下:
- 为什么这是错误的?
- 什么是 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 << " ";
}
在上面的例子中,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;
}
我有一些从 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 中你可以传递右值..但我显然在这里遗漏了一些东西。谁能解释一下:
- 为什么这是错误的?
- 什么是 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 << " ";
}
在上面的例子中,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;
}