take a look at the following code:
#include <algorithm>
#include <deque>
#include <iostream>
using namespace std;
int main()
{
deque<int> in {1,2,3};
deque<int> out;
// line in question
move(in.begin(), in.end(), out.begin());
for(auto i : out)
cout << i << endl;
return 0;
}
This will not move anything. Looking at the example here, one must write the line in question like this:
move(in.begin(), in.end(), std::back_inserter(out));
This makes sense in a way, as std::move expects its first two arguments to be InputInterators (which is satisfied here) and the third one to be an OutputIterator (which out.begin() is not).
What does actually happen if the original code is executed and move is passed an iterator that is not an OutputIterator? Why does C++'s type-safety not work here? And why is the construction of an output-iterator delegated to an external function, i.e. why does out.backInserter() not exist?
The original code tries to dereference and increment out.begin(). Since out is empty, that's a past-the-end iterator, and it can't be dereferenced or incremented. Doing so gives undefined behaviour.
std::move expects [...] the third one to be an OutputIterator (which out.begin() is not).
Yes it is. Specifically, it's a mutable random access iterator, which supports all the operations required of an output iterator, and more.
What does actually happen if the original code is executed and move is passed an iterator that is not an OutputIterator?
That would cause a compile error if the iterator didn't support the operations required of an output iterator needed by the function; or undefined behaviour if the operations existed but did something other than that required of an output iterator.
Why does C++'s type-safety not work here?
Because the type is correct. The incorrect runtime state (being a past-the-end iterator, not the start of a sequence with at least as many elements as the input range) can't be detected through the static type system.
why does out.backInserter() not exist?
That would have to be written separately for all sequence containers: both the standard ones, and any others you might define yourself. The generic function only has to be implemented once, in the standard library, to be usable for any container that supports push_back.
Related
In many examples of ostream_iterator I notice that an increment operation is used between writes:
e.g.
#include <iostream>
#include <iterator>
using namespace std;
int main() {
ostream_iterator<char> itr{ cout };
*itr = 'H';
++itr; // commenting out this line appears to yield the same output
*itr = 'i';
}
On the cpp reference increment (operator++) is listed as a no-op. Is there a reason to include it despite being a no-op? Is it a "best-practice" to include?
Why does ostream_iterator exist at all, when you can print directly to cout?
Answer: to be used with functions that expect an iterator.
There is a bunch of requirements that a type has to satisfy to be "an iterator" (and that those functions expect), and overloading ++ and * in a certain way is one of them.
Both * and ++ do nothing for an ostream iterator. If you're asking if you should call them or not, you don't need ostream_iterator in the first place.
Consider a possible implementation of std::copy:
template<class InputIt, class OutputIt>
OutputIt copy(InputIt first, InputIt last,
OutputIt d_first)
{
for (; first != last; (void)++first, (void)++d_first) {
*d_first = *first;
}
return d_first;
}
This code works with all output iterators. It also works with std::ostream_iterator. ++d_first is a noop in this case, but it still it has to be valid code, because for other output iterators it is needed.
Actually the major reason for std::ostream_iterator to exists is to be used with such generic algorithms, otherwise you could just write to the stream directly. Its a nice example of iterators being the glue between algorithms and container or more generally the data. Algorithms can be generic and need not care where the data is actually coming from or going to, because the iterator implements the special case.
Also from cppreference on std::ostream_iterator::operator++:
Does nothing. These operator overloads are provided to satisfy the
requirements of LegacyOutputIterator. They make it possible for the
expressions *iter++=value and *++iter=value to be used to output
(insert) a value into the underlying stream.
For your actual question:
Is there a reason to include it despite being a no-op? Is it a "best-practice" to include?
No. There is no point in incrementing the iterator when you know its a std::ostream_iterator. The only reason it can be incremented (which is a noop) is for generic code.
As you know, we can provide UnaryOperation and BinaryOperation for a lot of stl functions. Arguments of these methods can be defined by value, but in a lot of cases, we pass them by reference as follows:
Ret fun(const Type &a); // UnaryOperation
Ret fun(const Type1 &a, const Type2 &b); // BinaryOperation
Now I wonder if these callback arguments are guaranteed to be the same element as main data by standard or not. For example, is the following code valid in the standard?
#include <iostream>
#include <vector>
#include <execution>
#include <algorithm>
int main() {
std::vector<int> arr(10);
std::transform(std::execution::par, arr.begin(), arr.end(), arr.begin(),
[&arr](const int& v) -> int { return (&v - &arr[0]); });
for (const auto& v: arr)
std::cout << v << " ";
}
Because (&v - &arr[0]) will be valid if and only if v refers to original elements in arr.
The example as written exhibits undefined behavior. Parallel algorithms are given a dispensation to make arbitrary copies of elements of the sequence, under certain circumstances.
[algorithms.parallel.user]/1 Unless otherwise specified, function objects passed into parallel algorithms ... shall not ... rely on the identity of the provided objects.
[algorithms.parallel.exec]/3 Unless otherwise stated, implementations may make arbitrary copies of elements (with type T) from sequences where is_trivially_copy_constructible_v<T> and is_trivially_destructible_v<T> are true. [Note: This implies that user-supplied function objects should not rely on object identity of arguments for such input sequences. Users for whom the object identity of the arguments to these function objects is important should consider using a wrapping iterator that returns a non-copied implementation object such as reference_wrapper<T> (20.14.5) or some equivalent solution. —end note]
A strict reading of [algorithms.parallel.user]/1 suggests that the user-provided function must never rely on being given the original element and not a copy. The non-normative note in [algorithms.parallel.exec]/3 appears to suggest that it would be OK to so rely if the element type is not trivially copyable or destructible. In any case, the example uses int for element type, which is in fact trivially copyable and destructible, and for which the std::transform(par) implementation is clearly allowed to make copies.
For motivation, see P0518r1 "Allowing copies as arguments to function objects given to parallel algorithms in response to CH11"
Non-parallel algorithms, on the other hand, are not allowed to make copies of elements unless required by the algorithm's specification. Thus, for non-parallel std::transform, the user-provided function may indeed rely on being given a reference to the element of the source sequence.
[res.on.data.races]/5 A C++ standard library function shall not access objects indirectly accessible via its arguments or via elements of its container arguments except by invoking functions required by its specification on those container elements.
The specification of std::transform in [alg.transform] says that it should call op(*(first1 + (i - result))) for "every iterator i in the range [result, result + N)"; that is, it should pass the result of dereferencing the iterator into the source sequence directly to the user-provided function.
I'm studying C++ and I'm reading about STL containers,iterators and the operations that can be performed on them.
I know that every container type (or better, the corresponding template of which each type is an instance) defines a companio type that acts like a pointer-like type and it's called iterator. What I understand is that once you get an iterator to a container,performing operations like adding an element may invalidate that iterator,so I tried to test this statement with an example:
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> ivec={1,2,3,4,5,6,7,8,9,0};
auto beg=ivec.begin();
auto mid=ivec.begin()+ivec.size()/2;
while (beg != mid) {
if (*beg==2)
ivec.insert(beg,0);
++beg;
}
for (auto i:ivec)
cout<<i<<" ";
}
here,I'm simply contructing a vector of ints, brace initialize it,and performing a condition based operation,inserting an element in the first half of the container.
The code is flawed I think, because I'm initializing two iterator objects beg
and end and then I use them in the while statement as a condition.
BUT, if the code should change the contents of the container (and it sure does) what happens to the iterators?
The code seems to run just fine,it add a 0 in the ivec[1] position and prints the result.
What I thought is that the beg iterator would point to the newly added element and that the mid iterator would have pointed to the element before the formerly pointed to by mid (it's like the iterators point to the same memory locations while the underlying array,"slides" under.. unless it's reallocated that is)
Can someone explain me this behaviour??
When the standard says iterators are invalidated, this does not guarantee that they will be invalid in the sense of preventing your program from working. Using an invalid iterator is undefined behavior which is a huge and important topic in C++. It doesn't mean your program will crash, but it might. Your program might also do something else--the behavior is completely undefined.
Is there any way to convert a vector::iterator into a pointer without having access to the vector itself? This works only if the vector is available:
typedef vector<int> V;
int* to_pointer1( V& v, V::iterator t )
{
return v.data() + (t-v.begin() );
}
But how can this be done without having the vector around? This wouldn't work:
int* to_pointer2( V::iterator t )
{
// the obvious approach doesn't work for the end iterator
return &*t;
}
In fact I would have expected this to be a very basic and popular question but for some reason I couldn't find it.
In general you are not guaranteed that there is a pointer corresponding to an iterator that refers to an ordinary vector item.
In particular std::vector<bool> is specialized so that &*it won't even compile.
However, for other vectors it's only1 the formally-pedantic that stops you from doing &*it also for the end iterator. In C99 it is, as I recall, formally valid to do &*p when p is an end pointer, and a goal of std::vector is to support all ordinary raw array notation. If I needed this I would just ignore the formal-pedantic, and write code with a view towards some future revision of the standard that would remove that imagined obstacle.
So, in practice, just do &*it. :)
#include <iostream>
#include <vector>
using namespace std;
auto main() -> int
{
vector<int> x( 5 );
cout << &*x.begin() << " " << &*x.end() << endl;
cout << &*x.end() - &*x.begin() << endl; // Works nicely IN PRACTICE.
}
But do remember that you can't do this for vector<bool>.
1) In a comment elsewhere user pentadecagon remarks: “Try -D_GLIBCXX_DEBUG, as documented here: gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode_using.html.” g++ often provides some means of bringing any formal UB to the front, e.g. “optimization” of formally UB loops. A solution in this particular case is simply to not ask it to do this, but more generally one may have to explicitly ask it to not do it.
No, this is not currently possible. n3884 Contiguous Iterators: A Refinement of Random Access Iterators proposes a free function std::pointer_from which would satisfy your requirement:
Expression: std::pointer_from(a)
Return type: reference
Operational semantics: if a is dereferenceable, std::address_of(*a); otherwise, if a is reachable from a dereferenceable iterator b, std::pointer_from(b) + (a – b); otherwise a valid pointer value ([basic.compound]).
What you are asking is not possible for the end iterator; By definition, the end iterator points to a hypothetical element past the end of the array (i.e. de-referencing it, is always UB).
You said:
#Nick I want it to work for any valid iterator, including the end. It should be possible, because to_pointer1 also covers this case.
to_pointer1 doesn't cover this case. to_pointer1 returns an invalid memory address (Actually the address is probably valid, but there is no data there).
I use this template:
template<typename It>
inline auto ptr(It const&it) -> decltype(std::addressof(*it))
{ return std::addressof(*it); }
In practice, this will work for most iterators. Exceptions are objects where either *it is not defined (vector<bool>::iterator) or where it points to nowhere (rather than to an past-the-last element). For your particular purpose, it shall be okay, except for vector<bool> (when the concept of a pointer is not sensible).
I'm kinda stuck with using a set with a pointer delegate. My code is as follows:
void Graph::addNodes (NodeSet& nodes)
{
for (NodeSet::iterator pos = nodes.begin(); pos != nodes.end(); ++pos)
{
addNode(*pos);
}
}
Here NodeSet is defined as:
typedef std::set<Node_ptr, Node_ptr_Sorting_Predicate> NodeSet;
The above piece of code works perfectly on my windows machine, but when I run the same piece of code on a MAC, it gives me the following error:
no matching function for call to 'Graph::addNode(const boost::shared_ptr<Node>&)'
FYI, Node_ptr is of type: typedef boost::shared_ptr<Node> Node_ptr;
Can somebody please tell me why this is happening?
Ok, from your added information, the problem seems to be that addNode takes a Node_ptr per non-const reference, while what the compiler has to call the function is a const boost::shared_ptr<Node>& (note the const). Let me explain:
std::set is an associative container. Associative containers store their elements in some order, using the key element to define the ordering. If you would be allowed to change the key without the container knowing, you would invalidate the container's internal order. That's why I believe dereferencing a std::set<T>::iterator does not return an modifiable lvalue. (Which means you cannot alter the reference returned. For example, if you have an iterator pos into a std::set<int>, *pos=42 should not compile.)
The catch with this is that only modifiable lvalues will bind to a non-const reference. But what *pos returns isn't a modifiable lvalue and thus won't. (So, in my example, int& r = *pos; won't compile.) The reason is that, if this was allowed, you could change the sorting key through that non-const reference behind the container's back and mess up the container's internal ordering.
That is why the result of your *pos won't bind to a Node_ptr&. And that in turn is why the compiler cannot call your function.
Does your addNode() member function really alter the Node it's given? If not, it should take a const Node_ptr&.
If it does, you have a design problem. You cannot alter an element that's in a set. The only thing you can do is to remove it from the set, change it, and add it back in.
On a side note: VC9 indeed compiles the following piece of code:
#include <iostream>
#include <set>
#include <typeinfo>
#include <iterator>
int main()
{
std::set<int> set;
set.insert(5);
std::cout << *set.begin() << '\n';
*set.begin() = 3; // this is an error!
std::cout << *set.begin() << '\n';
return (0);
}
I believe this is an error in VC9. Comeau rejects it.
Here's how to solve riddles with a compiler not calling a function you think it should call or calling the wrong function from a set of overloads.
The function you thought it should call is Graph::addNode(Node_ptr&). The code that you thought should call it is
addNode(*pos);
Change that code so that it provides the exact parameter(s) required:
Node_ptr& tmp = *pos;
addNode(tmp);
Now the call should definitely compile (or call the right overload), and the compiler should bark if it thinks *pos cannot be assigned to to a Node_ptr&.
Usually this tactic helps me to find out what's wrong in such situations.
If memory serves, the original C++ spec (1998) permits std::set to return modifiable iterators. This carries with it a risk--the iterator might be used to modify the stored value, such that the ordering of the set is now broken. I believe subsequent versions of the spec have changed this, and now all set iterators are non-modifiable.
VC++ 2010 respects the new behaviour, and has non-modifiable set iterators (which is annoying, as it prevents making changes that don't change the ordering and which ought to be legal).
Prior versions, however, did not. This means that you can create functions that are not suitably annotated with const, which will cause problems on switching to different compilers. The solution is to add the necessary const changes. VC++ will still work (since non-const values can be implicitly made const anyway), and so will everything else.