Why cannot it get iterator as reference in c++? - c++

I am learning c++.
I have learned referencing allow us to avoid copying.
So I tried to get an iterator of a vector as reference to overwrite its elements.
// simplified function
std::vector<int>::iterator& GetIterator(std::vector<int>& vec) {
return vec.begin(); // compiler said that here is an error.
}
But compiler give me an error message and suggests to use const reference.
I would not like to use const.
Why cannot it get non-const reference?
Or how to avoid copying of iterator?
Thank you very much.

You can sometimes use a reference to avoid a copy - but here you need a copy. The result of vec.begin() is a temporary which ceases to exist when your function returns. If you want the value outside the function - and you do - then a copy is needed. (If you succeeded in forming a reference here it would be a "dangling reference", which is not a good thing.)
And in any case a lot of types are designed to be copied efficiently, and iterators are amongst those. And in fact the compiler can often avoid the copy for you, without you having to do any work at all - and this is one of those cases.
So the code without the reference is easier, correct, and already optimal.

vec.begin(); return a temporary object and is r-value. You can't bind a l-value reference to a r-value. After returning from GetIterator function, that temporary object will be destroyed and your reference will be dangled.
Return by value (std::vector<int>::iterator) instead of reference. Iterators are designed to be copyable and cheap to copy. Also compilers use Return value optimization to avoid cost of coping objects.

correct code
std::vector<int>::iterator GetIterator(std::vector<int>& vec) {
return vec.begin(); // compiler said that here is an error.
}
your & is extra.
The iterator is like int* and gets a address.
And std::vector< type >::iterator is like that.
So the vector.begin() is like &int so you can not bind it to iterator&

Related

std::move a const std::vector in a lambda capture

Motivation:
I'm trying to transfer a std::vector<std::unique_ptr<some_type>> to a different thread, via a lambda capture.
Since I need the vector to not be cleaned up when the function goes out of scope, I need to take it by value (and not by reference).
Since it's a vector of unique_ptrs, I need to move (and not copy) it into the capture.
I'm using a generalized lambda capture to move the vector while capturing.
Minimal program to illustrate the concept:
auto create_vector(){
std::vector<std::unique_ptr<int>> new_vector{};
new_vector.push_back(std::make_unique<int>(5));
return std::move(new_vector);
}
int main() {
const auto vec_const = create_vector();
[vec=std::move(vec_const)](){
std::cout << "lambda, vec size: " << vec.size() << std::endl;
}();
}
Issue:
If I'm using a const local vector, compilation fails due to attempting to copy the unique_ptrs.
However if I remove the const qualifier, the code compiles and runs well.
auto vec_const = create_vector();
Questions:
What's the reason for this? Does being const disable the "movability" of the vector? Why?
How would I ensure the constness of a vector in such a scenario?
Follow-up:
The comments and answers mention that a const type can't be moved from. Sounds reasonable, however the the compiler errors fail to make it clear. In this case I would expect one of two things:
The std::move(vec_const) should throw an error regarding moving from const (casting it to rvalue) being impossible.
The vector move-constructor telling me that it refuses to accept const rvalues.
Why don't those happen? Why does instead the assignment seems to just try to copy the unique_ptrs inside the vector (which is what I'd expect from the vectors copy-constructor)?
Moving is a disruptive operation: you conceptually change the content of the thing you move from.
So yes: a const object can (and should) not be moved from. That would change the original object, which makes its constness void.
In this case, vector has no vector(const vector&&), only vector(vector &&) (move constructor) and vector(const vector &) (copy constructor).
Overload resolution will only bind a call with const vector argument to the latter (lest const-correctness would be violated), so this will result in copying the contents.
I agree: error reporting sucks. It's hard to engineer an error report about vector when you hit a problem with unique_ptr. That's why the whole tail of required from ...., required from ... obliterates the view.
From your question, and your code, I can tell that you don't fully grasp the move semantics stuff:
you shouldn't move into a return value; a return value is already an rvalue, so there's no point.
std::move does not really move anything, it only changes the qualifier of the variable you want to 'move from', so that the right receiver can be selected (using 'binding' rules). It is the receiving function that actually changes the contents of the original object.
When you are moving something from A to B, then act of moving must necessarily mean that A gets modified, since after the move A may no longer have whatever was in A, originally. This is the whole purpose of move semantics: to provide an optimal implementation since the moved-from object is allowed to be modified: its contents getting transferred in some fast and mysterious way into B, leaving A in some valid, but unspecified, state.
Consequently, by definition, A cannot be const.

Is it costly to pass an initializer_list as a list by value?

I want to pass a std::list as a parameter to fn(std::list<int>), so I do fn({10, 21, 30}) and everybody is happy.
However, I've come to learn that one shouldn't pass list by value, cause it's costly. So, I redefine my fn as fn(std::list<int> &). Now, when I do the call fn({10, 21, 30}), I get an error: candidate function not viable: cannot convert initializer list argument to 'std::list<int> &'.
QUESTION TIME
Is the "you shall not pass an costly object by value" rule valid here? We aren't passing a list after all, but an initializer_list, no?
If the rule still applies, what's the easy fix here?
I guess my doubt comes from the fact that I don't know clearly what happens when one passes an initializer_list argument to a function that accepts a list.
Is list generated on the spot and then passed by value? If not, what is it that actually happens?
However, I've come to learn that one shouldn't pass list by value, cause it's costly.
That's not entirely accurate. If you need to pass in a list that the function can modify, where the modifications shouldn't be externally visible, you do want to pass a list by value. This gives the caller the ability to choose whether to copy or move from an existing list, so gives you the most reasonable flexibility.
If the modifications should be externally visible, you should prevent temporary list objects from being passed in, since passing in a temporary list object would prevent the caller from being able to see the changes made to the list. The flexibility to silently pass in temporary objects is the flexibility to shoot yourself in the foot. Don't make it too flexible.
If you need to pass in a list that the function will not modify, then const std::list<T> & is the type to use. This allows either lvalues or rvalues to be passed in. Since there won't be any update to the list, there is no need for the caller to see any update to the list, and there is no problem passing in temporary list objects. This again gives the caller the most reasonable flexibility.
Is the "you shall not pass an costly object by value" rule valid here? We aren't passing a list after all, but an initializer_list, no?
You're constructing a std::list from an initializer list. You're not copying that std::list object, but you are copying the list items from the initializer list to the std::list. If the copying of the list items is cheap, you don't need to worry about it. If the copying of the list items is expensive, then it should be up to the caller to construct the list in some other way, it still doesn't need to be something to worry about inside your function.
If the rule still applies, what's the easy fix here?
Both passing std::list by value or by const & allow the caller to avoid pointless copies. Which of those you should use depends on the results you want to achieve, as explained above.
Is list generated on the spot and then passed by value? If not, what is it that actually happens?
Passing the list by value constructs a new std::list object in the location of the function parameter, using the function argument to specify how to construct it. This may or may not involve a copy or a move of an existing std::list object, depending on what the caller specifies as the function argument.
The expression {10, 21, 30} will construct a initializer_list<int>
This in turn will be used to create a list<int>
That list will be a temporary and a temporarys will not bind to a
non-const reference.
One fix would be to change the prototype for you function to
fn(const std::list<int>&)
This means that you can't edit it inside the function, and you probably don't need to.
However, if you must edit the parameter inside the function, taking it by value would be appropriate.
Also note, don't optimize prematurely, you should always use idiomatic
constructs that clearly represents what you want do do, and for functions,
that almost always means parameters by const& and return by value.
This is easy to use right, hard to use wrong, and almost always fast enough.
Optimization should only be done after profiling, and only for the parts of the program that you have measured to need it.
Quoting the C++14 standard draft, (emphasis are mine)
18.9 Initializer lists [support.initlist]
2: An object of type initializer_list provides access to an array of
objects of type const E. [ Note: A pair of pointers or a pointer plus
a length would be obvious representations for initializer_list.
initializer_list is used to implement initializer lists as specified
in 8.5.4. Copying an initializer list does not copy the underlying
elements. —end note ]
std::list has a constructor which is used to construct from std::initializer_list. As you can see, it takes it by value.
list(initializer_list<T>, const Allocator& = Allocator());
If you are never going to modify your parameter, then fn(const std::list<int>&) will do just fine. Otherwise, fn(std::list<int>) will suffice well for.
To answer your questions:
Is the "you shall not pass an costly object by value" rule valid here?
We aren't passing a list after all, but an initializer_list, no?
std::initializer_list is not a costly object. But std::list<int> surely sounds like a costly object
If the rule still applies, what's the easy fix here?
Again, it's not costly
Is list generated on the spot and then passed by value? If not, what is it that actually happens?
Yes, it is... your list object is created on the spot at run-time right before the program enters your function scope
However, I've come to learn that one shouldn't pass list by value, cause it's costly. So, I redefine my fn as fn(std::list &). Now, when I do the call fn({10, 21, 30}), I get an error: candidate function not viable: cannot convert initializer list argument to 'std::list &'.
A way to fix the problem would be:
fn(std::list<int>& v) {
cout << v.size();
}
fn(std::list<int>&& v) {
fn(v);
}
Now fn({1, 2, 3 }); works as well (it will call the second overloaded function that accepts a list by rvalue ref, and then fn(v); calls the first one that accepts lvalue references.
fn(std::list<int> v)
{
}
The problem with this function is that it can be called like:
list<int> biglist;
fn(biglist);
And it will make a copy. And it will be slow. That's why you want to avoid it.
I would give you the following solutions:
Overloaded your fn function to accept both rvalues and lvalues
properly as shown before.
Only use the second function (the one that accepts only rvalue
references). The problem with this approach is that will throw a compile error even if it's called with a lvalue reference, which is something you want to allow.
Like the other answers and comments you can use a const reference to the list.
void fn(const std::list<int>& l)
{
for (auto it = l.begin(); it != l.end(); ++it)
{
*it; //do something
}
}
If this fn function is heavily used and you are worried about the overhead of constructing and destructing the temporary list object, you can create a second function that receives the initializer_list directly that doesn't involve any copying whatsoever. Using a profiler to catch such a performance hot spot is not trivial in many cases.
void fn(const std::initializer_list<int>& l)
{
for (auto it = l.begin(); it != l.end(); ++it)
{
*it; //do something
}
}
You can have std::list<> because in fact you're making temporary list and passing initializer_list by value is cheap. Also accessing that list later can be faster than a reference because you avoid dereferencing.
You could hack it by having const& std::list as parameter or like that
void foo( std::list<int> &list ) {}
int main() {
std::list<int> list{1,2,3};
foo( list );
}
List is created on function scope and this constructor is called
list (initializer_list<value_type> il,
const allocator_type& alloc = allocator_type())
So there's no passing list by value. But if you'll use that function and pass list as parameter it'll be passed by value.

Does &*x operation create a copy?

I had some problems converting a std::list<MyType>::iterator to MyType*. I have an array of MyType and as I loop through it, I call some void doSomething(const MyType* thing). Eventually I do this:
std::list<MyType> types = ... something here ...;
for( std::list<MyType>::const_iterator it = types.begin(), end = types.end(); it != end; ++it ) {
doSomething(&*it);
}
My question is, whether I do or do not create a memory copy of the MyType by this operation:
&*it
Trying to simply pass the iterator as if it was pointer, or to cast the iterator to pointer raised compiler errors.
No, the sequence of * and & does not create a copy:
* applied to an iterator produces a reference
& applied to a reference produces the address of the original
Neither of these two steps requires copying of MyType's data.
Note: When the presence of an overloaded operator & is a possibility, it is safer to use std::address_of(*it) expression.
One of the requirements for an iterator is that it be derefernceable -- i.e. *it be supported. See http://en.cppreference.com/w/cpp/concept/Iterator. Whether *it returns a reference or a copy is not specified.
In theory, an implementation could return a copy when using *it.
In practice, most implementations return a reference when using *it. Hence, you most likely won't be seeing a copy being made when using &*it.
As long as the iterator is valid it is doing what your want. And no, it does not create a copy.
Based on how your code is written, you are creating a copy of the pointer, but the memory being pointed to by the pointer should not be copied.
Broken down, you take the iterator object (it), you look at the MyType object referenced by the iterator (*it), and then get the address of this object (&*it). The pointer is then copied into the function and used as you desire.

what happens to lvalue passed in function as rvalue (c++)?

I have been wondering about that all day long and I can't find an answer to that specific case.
Main :
std::vector<MyObject*> myVector;
myVector.reserve(5);
myFunction(std::move(myVector));
myFunction :
void myFunction(std::vector<MyObject*> && givenVector){
std::vector<MyObject*> otherVector = givenVector;
std::cout << givenVector[0];
// do some stuff
}
My questions are :
in the main, is myVector destroyed by the function myFunction() because it is considered as an rvalue or does the compiler knows that it is also a lvalue and therefore performs a copy before sending it to myFunction ? What happens if I try to use the vector after the call to myFunction()?
inside the function myFunction() , is the vector givenVector destroyed when affected to otherVector ? if so, what happens when I try to print it ? if not is it useful to use rvalue in this function ?
Looks like duplicate.
myVector is not destroyed by the function myFunction(). It's unspecifed what should happen in general case with class with stealen resources.
givenVector is not destroyed when affected to otherVector. It's unspecifed what should happen in general case with class with stealen resources.
In order to be compilable, you should apply a std::move to your vector before you pass it to the function (--at least if no further overloads exists):
myFunction(std::move(myVector));
Then, inside the function, by
std::vector<MyObject*> otherVector = std::move(givenVector);
the move constructor of std::vector is called which basically moves all the content out of the vector (note however again the std::move on the right-hand side -- otherwise you'll get a copy). By this, the vector is not "destroyed". Even after the move it is still alive, yet in an unspecified state.
That means that those member functions which pose no specific condition on the state of the vector might be called, such as the destructor, the size() operator and so on. A pop_back() or a derefencing of a vector element however will likely fail.
See here for a more detailed explanation what you still can do with a moved-from object.
The code won't compile, since you try to bind an lvalue to an rvalue reference. You'll need to deliberately convert it to an rvalue:
myFunction(std::move(givenVector));
Simply doing this won't "destroy" the object; what happens to it depends on what the function does. Generally, functions which take rvalue references do so in order to move from the argument, in which case they might leave it in some valid but "empty" state, but won't destroy it.
Your code moves the vector to the local otherVector, leaving it empty. Then you try to print the first element of an empty vector, giving undefined behaviour.
No copy is performed. What happens to myVector depends on what myFunction does with it. You should consider objects that have been moved from as either being the same or being empty. You can assign new values and keep using it or destroy it.
myVector is fine. It is an lvalue and otherVector makes a copy of it. You most likely wanted to write otherVector = std::move(myVector);, in which case myVector should be empty. If you have an old implementation of the STL (that does not know about move semantics) a copy is performed and myVector is not changed. If that makes sense is for you to decide. You moved a given vector to a new vector, which can be useful. Printing an empty vector is not so useful.
If a function gets an argument by rvalue-reference, that does not mean it will be destructively used, only that it can be.
Destructive use means that the passed object is thereafter in some unspecified but valid state, fit only for re-initializing, mving, copying or destruction.
In the function, the argument has a name and thus is an lvalue.
To mark the place(s) where you want to take advantage of the licence to ruthlessly plunder it, you have to convert it to an rvalue-reference on passing it on, for example with std::move or std::forward, the latter mostly for templates.

What is the difference between these two parameters in C++?

I am new to C++ and currently am learning about templates and iterators.
I saw some code implementing custom iterators and I'm curious to know what the difference between these two iterator parameters is:
iterator & operator=(iterator i) { ... i.someVar }
bool operator==(const iterator & i) { ... i.someVar }
They implement the = and == operators for the particular iterator. Assuming the iterator class has a member variable 'someVar', why is one operator implemented using "iterator i" and another with "iterator & i"? Is there any difference between the two "i.someVar" expressions?
I googled a little and found this question
Address of array - difference between having an ampersand and no ampersand
to which the answer was "the array is converted to a pointer and its value is the address of the first thing in the array." I'm not sure this is related, but it seems like the only valid explanation I could find.
Thank you!
operator= takes its argument by value (a.k.a. by copy). operator == takes its argument by const reference (a.k.a. by address, albeit with a guarantee that the object will not be modified).
An iterator may be/contain a pointer into an array but it is not itself an array.
The ampersand (&) has different contextual meanings. Used in an expression, it behaves as an operator. Used in a declaration such as iterator & i, it forms part of the type iterator & and indicates that i is a reference, as opposed to an object.
For more discussion (with pictures!), see Pass by Reference / Value in C++ and What's the difference between passing by reference vs. passing by value? (this one is language agnostic).
the assignment operator = takes the iterator i as value, which means a copy of the original iterator is made and passed to the function so any changes applied to the iterator i inside the operator method won't affect the original.
the comparison operator == takes a constant reference, which denotes that the original object can't/shouldn't be changed in the method. This makes sense since a comparison operator usually only compares objects without changing them. The reference allows to pass a reference to the original iterator which lives outside the method. This means that the actual object won't be copied which is usually faster.
First, you don't have an address of an array here.
There's no semantic difference, unless you try to make a local change to the local variable i: iterator i will allow a local change, while const iterator & i will not.
Many people are used to writing const type & var for function parameters because passing by reference can be faster than by value, especially if type is big and expensive to copy, but in your case, an iterator should be small and cheap to copy, so there's no gain from avoiding copying. (Actually, having a local copy can enhance locality of reference and help optimization, so I would just pass small values by value (by copying).)