#include<vector>
#include<iostream>
using namespace std;
int main()
{
vector<int> vec = {1,2,3,4};
for(auto & it = vec.begin(); it != vec.end(); ++it)
{
cout << *it << endl;
}
}
Hello all, in C++ I use iterator by reference such as "auto & it" and the compiler return the error
" error: invalid initialization of non-const reference of type '__gnu_cxx::__normal_iterator >&' from an rvalue of type 'std::vector::iterator {aka __gnu_cxx::__normal_iterator >}'
for(auto & it = vec.begin(); it != vec.end(); ++it)
".
I know "auto it = vec.begin()" works fine but as we all know pass by reference will improve the efficiency in C++, so why this error occurs when I use "auto & it"?
so why this error occurs when I use "auto & it"?
See the return type of begin. It does not return a reference. It returns the iterator by value. Therefore the return value is a temporary in the rvalue category. Temporaries (rvalues) can not be bound to non-const lvalue references. This is what the error "invalid initialization of non-const reference of type ... from an rvalue of type ..." means.
Besides, modifying the begin pointer of the container would make little sense. It would have been silly for begin to return a reference.
Solution: Just create a copy of the iterator by declaring a non-reference value, just like you know works fine.
but as we all know pass by reference will improve the efficiency in C++
This is nonsense. An arbitrarily placed reference is unlikely to improve efficiency. Besides, you aren't trying to pass a reference here. You are trying to bind a reference to an object returned by a function.
Almost, if not always, iterators are very fast to copy (std::vector::iterator certainly is) and the indirection that you may potentially introduce with a reference may well be less efficient.
The std::vector::begin returns an rvalue (a temporary object). You cannot take a reference to a temporary object with auto& (non-const lvalue reference). If you wish to take the mentioned return value by reference, you can use auto&& instead of auto& but I don't recommend it.
Using auto&& will create a mutable rvalue reference to the return value of std::vector::begin. This will extend its lifetime but I recommend to use auto only.
Using rvalue references won't improve the efficiency of your code, the compiler will optimize out the copy/move of interator object returned from std::vector::begin by value.
auto keyword gets the type as temporary object from the expression (std::vector::begin)
since the it is temporary object and thus compiler cannot deduce to reference to it.
auto& ( non-const lvalue)
The best approach in you case to use without reference auto var instead auto &var
If you are force to use Reference
C++11 has introduced rvalue-reference, which can bind to temporary object change to && instead of & as below
vector<int> vec = {1,2,3,4};
for(auto && it = vec.begin(); it != vec.end(); ++it)
{
cout << *it << endl;
}
Related
In these questions (also here) there is recommendation to write code like:
for( auto&& v : arr )
But doesn't moving the data from arr into v clobber the array arr? How can this style of loop be safe at all?
First, auto&& is not an rvalue reference. It is called a forwarding reference and it can either be an lvalue reference, or rvalue reference, depending on if the initializer is an lvalue or rvalue.
In this case arr is a named object, so auto&& is actually the same as auto& in this case.
Lets pretend then arr is an rvalue and auto&& is actually an rvalue reference, you still don't have to worry. A reference is just an alias to the source object, it doesn't actually do anything to the source object. No move will happen as no move operation is taking place. All you are doing is referring to the element. You'd need code in the loop like
something = std::move(v); // move is required here, even though v is an rvalue reference
// because since it has a name, it is an lvalue
in order to move what v is referring to.
With the loop as is, the biggest concern is that you can change the values stored in arr since v has non-const access to arr. Using
for (const auto& v : arr)
lets you still accept lvalues and rvalues, but also does not allow you to modify the elements.
std::vector<std::string> func(int num) {
std::vector<std::string> vec;
int sum = 0;
for(int i = 0; i < num; i++) {
sum +=i;
std::string s = std::to_string(sum);
vec.emplace_back(s);
}
return vec;
}
what's the difference between the code below about using rvalue and lvalue reference.
std::vector<std::string> res = func(10); // (case 1)
std::vector<std::string> &&res = func(10); // (case 2)
std::vector<std::string> &res = func(10); // I got an error!, case3
const std::vector<std::string> &res = func(10); // case4
Question:
can the rvalue reference(case 2) save a memory copy? than case1
why lvalue reference(case3) got an error but it work with the const (case4)?
When a prvalue (a "temporary") is bound to an rvalue reference or a const lvalue reference, the lifetime of the temporary is extended to the lifetime of the reference.
a non-const lvalue reference does not extend the lifetime of a prvalue.
can the rvalue reference(case 2) save a memory copy? than case1
It preserves the lifetime of the single returned object. See 1.
why lvalue reference(case3) got an error
See 2.
but it work with the const (case4)?
See 1.
can the rvalue reference(case 2) save a memory copy? than case1
If you mean, "does case 2 avoid some copy that case 1 has", then the answer is: No. Both are effectively identical if res is an automatic variable. I recommend writing the case 1 because it is simpler and less confusing.
Firstly, there is a move from vec to the returned value. In practice, this move can be elided by an optimiser. Such optimisation is known as NRVO (Named Return Value Optimisation).
Since C++17: The is initialised directly by the move mentioned above (which may have been elided) in both cases.
Pre-C++17: There is another move from the return value to the initialised object in both cases. This move can also be elided in practice.
why lvalue reference(case3) got an error but it work with the const (case4)?
Because lvalue references to non-const may not be bound to rvalues.
#include <iostream>
using namespace std;
int main()
{
int i = 0;
cout << &i << endl;
const auto &ref = (short&&)i;
cout << &ref << endl;
return 0;
}
Why is &i different from &ref? (short&)i doesn't cause this problem. Does (short&&)i generate a temporary variable?
It's because you're doing a different type of cast. The C style explicit conversion cast does always a static cast, if it could be interpreted as a static cast; otherwise it does a reinterpret cast. And/or const cast as needed.
(short&&)i is a static cast because it can be interpreted as static_cast<short&&>(i). It creates a temporary short object, to which ref is bound. Being a different object, it has a different address.
(short&)i is a reinterpret cast because it cannot be interpreted as static_cast<short&>(i) which is ill formed. It reinterprets the int reference as short reference, and ref is bound to the the same object. Note that accessing the object through this reference would have undefined behaviour.
This creates a lvalue reference to a thing that exists:
const auto& ref = i;
The expressions &ref and &i will therefore give the same result.
This is also true of:
const auto& ref = (int&)i;
which is basically the same thing.
However, casting to something that is not a lvalue reference to T (so, to a value, or to an rvalue reference of another type!) must create a temporary; this temporary undergoes lifetime extension when bound to ref. But now ref does not "refer to" i, so the address-of results will differ.
It's actually a little more complicated than that, but you get the idea. Besides, don't write code like this! An int is not a short and you can't pretend that it is.
Apparently it creates a temporary.
Actually the compiler will tell you itself.
Try this:
auto &ref = (short&&)i;
cout << &ref << endl;
The error says:
error: non-const lvalue reference to type 'short' cannot bind to a
temporary of type 'short'
Test code here.
(short&&)i creates a temporary, so you take address of an other object, so address might differ.
I would like to create a custom container Container that stores data in individual arrays. However, to facilitate easy iterations over the container, I provide a 'view' on the container by overloading operator[] and return a single struct Value that holds all container variables as references to the actual container. This is what I got so far:
#include <iostream>
using namespace std;
struct Value {
Value(int& data) : data_(data) { }
int& data() { return data_; }
int& data_;
};
struct Container {
Value makeValue(int i) { return Value(data_[i]); } // EDIT 1
Value&& operator[](int i) {
// return std::forward<Value>(Value(data_[i]));
return std::forward<Value>(makeValue(i)); // EDIT 1
}
int data_[5] = {1, 2, 3, 4, 5};
};
int main(int, char**)
{
// Create and output temporary
Container c;
cout << c[2].data() << endl; // Output: 3 - OK!
// Create, modify and output copy
Value v = c[2];
cout << v.data() << endl; // Output: 3 - OK!
v.data() = 8;
cout << v.data() << endl; // Output: 8 - OK!
// Create and output reference
Value&& vv = c[2];
cout << vv.data() << endl; // Output: 8 - OK, but weird:
// shouldn't this be a dangling reference?
cout << vv.data() << endl; // Output: 468319288 - Bad, but that's expected...
}
The code above is working as far as I can tell, but I'm wondering if I use the best approach here:
Is it correct to return the Value as an rvalue reference if I want to avoid unnecessary copying?
Is the use of std::forward correct? Should I use std::move (both will work in this example) or something else?
The output of the compiled program is stated in the comments. Is there any way I can avoid the dangling reference when I declare Value&& vv... (or even forbid it syntactically)?
EDIT 1
I made a small change to the source code so that the Value instance is not directly created in the operator[] method but in another helper function. Would that change anything? Should I use the makeValue(int i) method as shown or do I need to use std::move/std::forward in here?
Is it correct to return the Value as an rvalue reference if I want to avoid unnecessary copying?
No. Returning rvalue references from something that isn't a helper like std::move or std::forward is flat-out wrong. Rvalue references are still references. Returning a reference to a temporary or a local variable has always been wrong and it still is wrong. These are the same C++ rules of old.
Is the use of std::forward correct? Should I use std::move (both will work in this example) or something else?
The answer to the previous question kinda makes this one moot.
The output of the compiled program is stated in the comments. Is there any way I can avoid the dangling reference when I declare Value&& vv... (or even forbid it syntactically)?
It's not the Value&& vv = c[2]; part that creates a dangling reference. It's operator[] itself: see answer to the first question.
Rvalue references change pretty much nothing in this case. Just do things as you would have always done:
Value operator[](int i) {
return Value(data_[i]);
}
Any compiler worth using will optimise this into a direct initialisation of the return value without any copies or moves or anything. With dumb/worthless/weird/experimental compilers it will at worst involve a move (but why would anyone use such a thing for serious stuff?).
So, the line Value v = c[2]; will initialise v directly. The line Value&& vv = c[2]; will initialise a temporary and bind it to the rvalue reference variable. These have the same property as const& used to, and they extend the lifetime of the temporary to the lifetime of the reference, so it wouldn't be dangling.
In sum, the same old C++ of always still works, and still gives results that are both correct and performant. Do not forget it.
Returning a reference to a temporary objects, even if it is an r-value reference, is always wrong! By the time you access the object it will be gone. In that case it also doesn't do what you want it to do, anyway: if you want to avoid unnecessary copies, have one return statement returning a temporary! Copy/move elision will take care of the object not being copied:
Value operator[](int i) {
return Value(data_[i]);
}
Passing the temporary object through a function will inhibit copy/move elision and not copying/moving is even less work than moving.
Today I came to know that references are not reseatable
Consider the code:
map<int,int> z;
z.insert(make_pair(1,2));
z.insert(make_pair(3,5));
z.insert(make_pair(4,6));
auto ref = z.at(1);
ref = z.at(3);
std::map::at returns a reference to the mapped value of the requested element, implies ref is a reference. Why is it allowed to be reassigned(as references cannot be re-binded). What is happening here.
auto doesn't make reference types. The expression z.at(1) is an lvalue of type int, so ref is also an int.
(If you wanted a reference, you'd have to say auto & or auto && (or in C++14 decltype(auto)).)