Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing rvalue as reference

I have some Qt code that I downloaded from my svn repo. It's a while since I worked on it but I am sure it used to compile.

I have a new version of Qt and compiler (to what I had in the last time). My current compiler is: mingw 4.9.2 32-bit.

So here is my problem code:

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

Where:

typedef std::vector<uint8_t> uint8Vect_t;

and

uint8Vect_t encodeData(uint8Vect_t &dataBuff);

So you can see here that I have a function encodeData() which takes a parameter uint8Vect_t & (pass by ref). I am passing a temporary variable (an rvalue I think) created using the std::vector constructor (one of which takes two iterators) from the QByteArray dataBlock iterators (which I have tested works).

However, I get the error:

../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&}'

Basically it's saying that I cannot convert from uint8Vect_t to uint8Vect_t&. But if I pass a variable of type uint8Vect_t into the function (rather then the return value of the contructor / temp variable) then this works ok.

I thought in c++11 you can pass rvalues.. but I am obviously missing something here. Can anyone explain:

  1. Why this is wrong?
  2. What is an efficient/elegant (readable) solution?
like image 977
code_fodder Avatar asked Jan 07 '16 12:01

code_fodder


3 Answers

Your issue is

uint8Vect_t encodeData(uint8Vect_t &dataBuff);

Here you are taking a reference to a uint8Vect_t. That works well with normal variables but uint8Vect_t(dataBlock.begin(), dataBlock.end()) is a temporary object and cannot be bound to lvalue reference.

If encodeData() does not change dataBuff then the simplest solution is to take a const & which can bind to a temproary.

uint8Vect_t encodeData(const uint8Vect_t &dataBuff);

If you have to change the contents of dataBuff then you would have to write another version of encodeData() that takes an rvalue reference

uint8Vect_t encodeData(uint8Vect_t &&dataBuff);

This will allow the function to bind to the temporary vector and you can work on it in the function as you would a normal vector.


I believe the reason you are seeing this is that your old compiler was a version of Microsoft Visual Studio. MSVS has a non standard extension that is on by default that allows temporary objects to bind to a lvalue reference. You can read more about it at: Non-const reference bound to temporary, Visual Studio bug?


Adding this to show you how you could change encodeData() to take an rvalue reference without having to write a new function.

#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

In the above example modify({ 1,2,3,4,5 }) calls modify(std::vector<int>&& foo) and then in the function foo is an lvaue. We then return the result of passing the "new" lvalue to modify(std::vector<int>& foo) which then returns a modified vector.

like image 80
NathanOliver Avatar answered Nov 15 '22 18:11

NathanOliver


When you use

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

the vector you pass into the function is a temporary object, and references can't bind to temporary objects.

The simple solution, if the function doesn't modify the argument, is to make it a reference to a constant object:

uint8Vect_t encodeData(uint8Vect_t const& dataBuff);

References to constant objects can bind to temporary objects.

like image 25
Some programmer dude Avatar answered Nov 15 '22 17:11

Some programmer dude


What do you want to do with/to the object you are passing in?

When you take it as uint8Vect_t &dataBuff that should mean you want to make lasting modifications to it, which makes no sense if it is a temporary.

When you take it as uint8Vect_t const&dataBuff that should mean you want to copy from it and not modify it, which is probably what you want.

When you take it as uint8Vect_t dataBuff that should mean you need your own local temporary copy of it, to use as you wish and then throw away, and that should be important enough to be worth the cost of copying.

When you take it as uint8Vect_t &&dataBuff that should mean you want to make non lasting modifications (such as content stealing) from a temporary object that the caller is effectively promising to throw away after you are done with it.

That last choice is the one new in C++11 for passing rvalues.

like image 27
JSF Avatar answered Nov 15 '22 18:11

JSF