Passing rvalue as reference - c++

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:
Why this is wrong?
What is an efficient/elegant (readable) solution?

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.

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.

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.

Return value of any function is an temporary object(rvalue) and you can't pass temporary object as reference.
below code will generate same error as we are trying to pass "saurabh"(temp object) as reference type.
i.e.
void fun(string& name){
//statements;
}
int main(){
fun("Saurabh");
return 0;
}

Related

Move semantics with std::vector misunderstanding when passing to a function

I try to understand the concept of move semantic and did some tests. I have the following function:
// testing the move semantic when passing
// argument to be modified without copying
void process_copy( std::vector<int>&& aVec)
{
std::cout << "Move semantic\n";
aVec.push_back(42);
}
now in the main function, the following:
int main()
{
std::vector<int> w_vec = {1,2,3,4,5};
process_copy( std::move(w_vec));
}
I expect that the w_vec is now empty since I pass it with move cast (cast lvalue to rvalue). But the result is w_vec contains 6 elements now (42 has been added to the vector).
Something that I miss? Is std::vector is a moveable object?
An image of my CoreCpp 2019 t-shirt might be relevant here.
This is the front.
It's interesting to note that this behavior of std::move was a bit surprising even for Howard Hinnant himself, the man behind rvalue and move semantics. However he got a very thorough answer there.
What you may be missing is that you are just binding an rvalue reference to the vector you pass; you are moving no vector object at all.
The process_copy() parameter is of type std::vector<int>&&, i.e., an rvalue reference to std::vetor<int>:
void process_copy(std::vector<int>&& aVec)
By using std::move() when calling process_copy() as in:
process_copy(std::move(w_vec));
You are just making it possible to bind this reference – i.e., process_copy()'s parameter, aVec – to the argument – i.e., the vector w_vec. That is, the following does not compile:
process_copy(w_vec);
Because you can't bind an rvalue reference (aVec) to an lvalue (w_vec).
If you want the vector argument w_vec to be moved when calling process_copy(), then you could have the function to take an std::vector<int> by value instead and move construct this parameter at the moment of calling the function:
void process_copy(std::vector<int> aVec) // <-- not a reference
{
aVec.push_back(42);
}
By marking the vector argument with std::move() when calling this process_copy(), the parameter aVec will be move constructed – so the vector argument will end up in a moved-from state.

Is an argument copied to the resulting temporary when both the function parameter and return types are references?

Chapter 6.3.2 of the book C++ Primer says the following:
The return value is used to initialize a temporary at the call site, and that temporary is the
result of the function call.
Later, it gives an example for returning reference values with the explanation below:
const string &shorterString(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
The parameters and return type are references to const string. The strings are not copied
when the function is called or when the result is returned.
Does this mean that the temporary resulting from this function call is initialized from a reference to the first or the second argument? If so, doesn't that mean that the argument does get copied into the temporary?
Does this mean that the temporary resulting from this function call is initialized from a reference to the first or the second argument?
Yes.
As the logic in the function suggests, wether the temporary is initialized from the first argument or the second argument depends on the value of s1.size() <= s2.size().
If so, doesn't that mean that the argument does get copied into the temporary?
Yes.
However, it's worth noting that the temporary itself is also a const string&. It does not require a copy of a string object.
The return value is used to initialize a temporary at the call site, and that temporary is the result of the function call.
The use of "temporary" is a bit misleading, and as of C++17 - false.
Up until C++17, the function could theoretically have created a temporary which is the return value. But the compiler was allowed to skip the creation of such a temporary, and just initialize/construct at the call site, e.g. if you wrote:
const std::string& foo { shorterString(my_string1, my_string2) };
then foo would have been initialized by the code inside shorterString directly (even if the function wasn't inlined). Granted, this doesn't matter all that much for const std::string&, but it sure does matter for types whose construction has side-effects; and for types which are non-copyable/non-movable.
Beginning in C++17, this is no longer an "optimization" - it is guaranteed in the standard that no temporary is constructed/initialized. See this blog post for a detailed discussion.

Pass rvalue by reference

I am struggling to turn this piece of code into a one-liner:
std::string filename = "cats";
std::shared_ptr<bmd2::Bmd2Dataset> ptr (new bmd2::Bmd2Dataset(filename, bmd2::File::FM_WRITE));
bmd2::bmd2Session::getInstance().addDataset(ptr);
The above works great -- but can I call addDataset without creating the lvalues (lines 1 + 2)?
Here is the declaration of addDataset():
int addDataset(std::shared_ptr<bmd2::Bmd2Dataset> &) throw (bmd2Exception);
bmd2::bmd2Session::getInstance().addDataset(
std::make_shared<bmd2::Bmd2Dataset>("cats", bmd2::File::FM_WRITE)
);
One statementer. :)
Interesting. For learning purposes, can you tell me if you can accomplish this without the make_shared() function call?
You can, using C++11's brace initialization syntax. Assuming addDataset isn't a template and has std::shared_ptr<bmd2::Bmd2Dataset> as its parameter,
bmd2::bmd2Session::getInstance().addDataset(
{ new bmd2::Bmd2Dataset("cats", bmd2::File::FM_WRITE) }
);
UPDATE: Oops. So addDataset() actually accepts an l-value reference why didn't you say so immediately?. addDataset()'s parameter can't bind to r-value parameters, which in the two above examples are. To solve this, you can either:
Make addDataset()'s parameter a const reference
int addDataset(const std::shared_ptr<bmd2::Bmd2Dataset> &) throw (bmd2Exception);
// ^^^^^
Do this if you don't need to modify the passed shared_ptr argument (i.e. modifying it so it points to another object).
Use your older method, though passing directly "cat" into the the new expression. UPDATE: Use std::make_shared!
auto ptr = std::make_shared<bmd2::Bmd2Dataset>("cats", bmd2::File::FM_WRITE);
bmd2::bmd2Session::getInstance().addDataset(ptr);
// ptr might now point to some other object
Do this if you want the passed argument's (ptr) value to be changed (i.e. after the call it might now point to a new object).

c++: const reference to an rvalue

//old school '98 c++, no C++0x stuff
std::string getPath();
void doSomething()
{
const std::string &path = getPath(); //const reference to an rvalue
... // doSomething with path
}
void doSomething2()
{
const std::string path = getPath(); //regular variable
... // doSomething with path
}
what are the differences between doSomething and doSomething2 and which one is prefferable?
Is it safe to use const reference to returned rvalue in doSomething?
Does doSomething2 create a copy of returned rvalue, is compiler allowed to do rvalue optimization here?
Is it safe to use const reference to returned rvalue in doSomething?
Yes, this is perfectly fine. The language has an specific clause that guarantees that if you bind a reference to an rvalue a temporary is created and the lifetime is extended until the end of the scope where the reference is created.
Does doSomething2 create a copy of returned rvalue, is compiler allowed to do rvalue optimization here?
In both cases the cost is the same, as the compiler will do RVO (return value optimization)
what are the differences between doSomething and doSomething2 and which one is prefferable?
The main difference is that in one case you are giving a name to the real object and in the other case the name is given to a reference. The compiler should generate exactly the same code in both versions.
That being said, I find the use of the const& to be misleading. It gives the impression that the local function has a reference to some object somewhere, but it really has a copy (with no name). To realize that this is a copy the maintainer will have to look and verify the signature of the function that is being called. In the case of the value the behavior is more obvious: the function maintains a copy of whatever is returned by the function (which might be a value or reference).
The second difference is that you are only legally allowed to bind a const reference, which means that the function cannot modify the object. In the case of storing a value, the type can be non-const and the function could modify that object, so the approach is more flexible.
In C++03 the only reason to use the const& trick is in the case where you know that the function returns a value, you know that the type derives from a well known base, and you don't want to name the type [Take a look at ScopeGuard]. In C++11 that use is no longer important, as you can just use auto to let the compiler figure out the type automatically.
(from other comments it seems this might be C++ standards version dependant as to guarentees when you bind a reference to an rvalue about its lifespan - this answer reflects C++ classic)
doSomething has a reference to what?
doSomething2 has a copy of something that may not exists anymore which is fine
lets look at getPath():
in general it will take the return value, copy it to the stack and then copy that to the lhs
it can just assign directly to the lhs
it can return "hello" which will create a string on the stack - this cannot be referenced because it is temporary (the problem with doSomething)
it can return a static const string - in which case it can be completely inlined and assigned to the lhs
The reference version could have a dangling reference if the std::string was allocated inside the function getPath() and destroyed when returning from that function.
Then, it's dangerous usage.
If you had a class holding that string and the class outlives the scope of that const std::string & reference, something as MyClass::getPath(), then, in that case, the reference wouldn't be dangling.
The second version will always work.

How does wrapping a pointer work with rvalue references?

std::shared_ptr::operator* returns by lvalue reference, and the answer given on overloading pointer like operations here says that the convention is to return by lvalue reference. However, when I'm using the following code, I get error C2664: 'AdjacencyList::addVertex' : cannot convert parameter 1 from 'AdjacencyList::vertex_type' to 'AdjacencyList::vertex_type &&': You cannot bind an lvalue to an rvalue reference:
std::shared_ptr<vertex_type> AdjacencyList::addVertex(vertex_type&& v)
{
auto existingVertex(findVertex(v));
if (!existingVertex.isValid())
{
existingVertex = std::make_shared<vertex_type>(std::forward<vertex_type>(v))
m_vertices.push_back(existingVertex);
}
return existingVertex;
};
AdjacencyList minimumSpanningTree;
// startVertex is a shared_ptr to a vertex returned from a previous call of addVertex
// on another AdjacencyList object
const auto mstStartVertex(minimumSpanningTree.addVertex(*startVertex));
Should I provide AdjacencyList::addVertex(const vertex_type& v) or change the code at the bottom of the above block to make a copy of the vertex before passing to addVertex?
AdjacencyList minimumSpanningTree;
Vertex s(*startVertex);
const auto mstStartVertex(minimumSpanningTree.addVertex(std::move(s)));
I would think that you should return a copy from your operator*, as the sematics of the std::weak_ptr suggest that you can not guarantee that a returned reference would stay valid. Since the returned copy is then given to a function which can move it somewhere else, it should also be efficient enough, since addVertex looks like it would require a copy anyways, i.e., if you would create an overload of addVertex, it will create a copy of the passed const reference internally, would it?
The most efficient approach in terms of redundant copies is to provide rvalue and const reference overloads:
std::shared_ptr<vertex_type> AdjacencyList::addVertex(vertex_type&&);
std::shared_ptr<vertex_type> AdjacencyList::addVertex(const vertex_type&);
To eliminate the redundant code, you can forward to a template method or to a concrete method taking a bool flag and performing const_cast as appropriate.
If the overhead of copying the Vertex object is minimal compared to the cost of increased code, and if the if block is usually or often entered, then the redundant copy will make your code clearer. Your second suggested call will work better if you just create a prvalue temporary that doesn't need to be moved:
const auto mstStartVertex(minimumSpanningTree.addVertex(Vertex{*startVertex}));
However in that case you might as well create the temporary in the call itself, by providing a single value overload (How to reduce redundant code when adding new c++0x rvalue reference operator overloads):
std::shared_ptr<vertex_type> AdjacencyList::addVertex(vertex_type);