List of Pairs iterator in c++ - c++

#include <bits/stdc++.h>
using namespace std;
int main() {
unordered_map< int,list<pair<int,int>>> adjList;
adjList[1].push_back(make_pair(2,2));
adjList[1].push_back(make_pair(4,-1));
adjList[0].push_back(make_pair(1,3));
for(pair<int,int> neighbour : adjList[1]){
pair<int,int>* it = &neighbour;
std::advance (it,1);
cout<<it->first<<" "; //1)showing random value twice instead of 4(-1113715584 -1113715584)
}
for (list<pair<int,int>>::iterator i=adjList[1].begin(); i!=adjList[1].end(); i++)
cout<<*i.first; //2)list<std::pair<int, int> >::iterator'has no member named 'first'
}
}
it is showing correct value(2,4) without {std::advance (it,1);} which is supposed to take iterator to next head but it is showing some random value twice every time.
error : 'std::__cxx11::list<std::pair<int, int> >::iterator' {aka 'struct std::_List_iterator<std::pair<int, int> >'} has no member named 'first'
cout<<*i.first;

You are invoking undefined beahvior here:
pair<int,int>* it = &neighbour;
std::advance (it,1);
cout<<it->first<<" ";
It is the same problem as with
std::pair<int,int> p;
std::pair<int,int>* ptr = &p;
ptr += 1; // ok-ish
std::cout << ptr->first; // undefined behavior !!
You can only increment a pointer to get a valid pointer when the pointer points to an element in an array. Incrementing the pointer to a single object is fine, because sloppy speaking a single pair can be considered as an array of size 1 and it is allowed to get a pointer one past the last element of an array. However, you shall not dereference that pointer.
A std::list does not store its elements in contiguous memory. You could get a pointer to an element in eg a std::vector and advance it to get a pointer to the next element. However, the address you are taking is actually not to the element in the list. When you write
for(pair<int,int> neighbour : adjList[1]){
Then neighbour is a copy of the element in the list. It is a single pair, completely unrelated to the list. If you had used
for(pair<int,int>& neighbour : adjList[1]){
Then neighbour would be a referece to the element in the list, but your code would still be wrong for the reasons explained above.
The second error is due to a typo. *i.first is *(i.first) but you want (*i).first or i->first.
Last, but not least, consider that std::make_pair has much fewer use-cases than it had before C++11. Your case isn't one of them, you can simply write:
adjList[1].push_back({2,2});
Moreover, I suggest you to read Why should I not #include <bits/stdc++.h>? and Why is “using namespace std;” considered bad practice?.

Related

Problems referencing one past last element on std::vector<> using operator[]

Given this valid C or C++ code
int x() {
int numbers[3]; // Lets suppose numbers are filled in with values
int sum = 0;
for (const int* p = &numbers[0]; p != &numbers[3]; ++p)
sum += *p;
return sum;
}
This code uses pointer arithmetic and as far as I know it is valid to have a pointer point to the one past last element inside an array, referencing that pointer is unspecified, but we can have a pointer point to that position.
So, &p[0], &p[1], &p[2] and &p[3] are valid pointers, and p[0], p[1] and p[2] are valid values.
If I replace the int array with a std::vector<int> everything should be fine, we get this code
#include <vector>
int x() {
std::vector<int> numbers(3);
int sum = 0;
for (const int* p = &numbers[0]; p != &numbers[3]; ++p)
sum += *p;
return sum;
}
But running under Visual C++ 2017 in DEBUG mode I get this exception "vector subscript out of range", that is triggered from MS STL library because the implementation assumes that using operator[] we are automatically referencing the underlaying value, which is not the case.
This is MS STL code that do the bounds check...
_NODISCARD _Ty& operator[](const size_type _Pos)
{ // subscript mutable sequence
#if _ITERATOR_DEBUG_LEVEL == 2
if (size() <= _Pos)
{ // report error
_DEBUG_ERROR("vector subscript out of range");
}
#elif _ITERATOR_DEBUG_LEVEL == 1
_SCL_SECURE_VALIDATE_RANGE(_Pos < size());
#endif /* _ITERATOR_DEBUG_LEVEL */
return (this->_Myfirst()[_Pos]);
}
I don´t get the error if I replace &numbers[0] and &numbers[3] with numbers.begin() and numbers.end().
I agree this is really ugly code, but I simplify the real code just to expose the bug.
The original code was using &vec[0] on a vector with zero elements.
So my question is:
Is this a BUG on Microsoft Visual C++ STL implementation or there is some restriction on operator[] for vectors<>?
I know that replacing [] by at() would be a bug, but I understand &vec[size] should still be valid for std::vector<>
It has been a grey area whether obtaining a pointer to the one past the last element with &p[n] is well defined.
However, a pointer to the one past the last element is well defined.
You can avoid such errors by using plain pointers arithmetic:
for (const int* p = numbers.data(); p != numbers.data() + 3; ++p)
Or, more generically, iterators:
using std::begin;
using std::end;
for(auto p = begin(v), q = end(v); p != q; ++p)
Or using a range for loop:
for(auto const& element : v)
There is no good reason to use v[v.size()], really.
The debug iterators library in Visual C++ is correctly reporting the problem, and the problem isn't subtle.
According to the standard, [sequence.reqmts] Table 101, the expression a[n] for a sequence container a of a type providing operator[] (std::vector does, as does basic_string, deque, and array), has the operational semantics of *(a.begin()+n). However, in the case you're running, a.begin()+n will be equivalent to a.end(). Therefore, the result is *(a.end()) before applying the address-of operator.
Dereferencing the end() iterator of a container invokes undefined behavior. Visual C++ is correct in reporting the assertion, and you would do well to change your enumeration strategy.
Dereferencing a past-last element results in undefined behaviour. If you declare std::vector<int> numbers(3) then the last element you are allowed to access is numbers[2]. Same story with raw arrays.
Avoid using raw arrays if you can:
int x() {
std::vector<int> numbers(3);
//...
int sum = 0;
for (auto value : numbers)
sum += value;
return sum;
}
So, &p[0], &p[1], &p[2] and &p[3] are valid pointers
No. The subscript operator (p[x]) for arrays is syntactic sugar for *(p+x), so &p[3] is actually doing &(*(p+3)) but *(p+3) is undefined behavior!!
If all you want is the address of one past last, p+3 is perfectly valid. This will use pointer arithmetic and not dereference anything.
If I replace the int array with a std::vector everything should be fine
Again, no! If you try to dereference a memory location you haven't allocated, you will get undefined behavior. std::vector says nothing about v[v.length()] being allocated for you, so this is undefined behavior.
the implementation asumes that using operator[] we are automatically referencing the underlaying value, wich is not the case
Yes it is!! Read the cppreference page on operator[]: "Returns a reference to the element at specified location pos. No bounds checking is performed." Just like with the raw arrays, the subscript operator here returns a reference to the underlying value, meaning there's a dereference step involved here!

How do you use a range-based for loop on the values of a std::map?

I'm trying to use std::map::operator[] to iterate over the values of a std::map with a range-based for loop, but the following doesn't compile:
#include <iostream> // cout, endl
#include <map> // map
#include <set> // set
using namespace std;
int main () {
using namespace std;
const map<int, set<int>> m {{2, {201, 202}}, {3, {301, 302}}};
for (int v : m[2])
cout << v << endl;
return 0;
}
Here's the compiler's error message:
Test.c++:18:19: error: no viable overloaded operator[] for type 'const map<int, set<int> >'
for (int v : m[2])
The followup question is, Given that there are two versions of at(), why aren't there two versions of []?
map::operator[] inserts a new element into the map if the key isn't found, hence it cannot be const, and cannot be called on a const map.
Use m.at(2) instead.
operator[] will do one of two things. It will find the element at that location if it exists and return it. If there is no element at that location, it will value-initialize it, and then return it.
As such, it is neither logically nor programmatically const.
While you may say "but there is an element 2", the constness of an operation depends only on the types of the arguments, not the value of the arguments. And m[int] isn't guaranteed to have a valid element there.
To fix this, you can replace m[2] with m.find(2)->second. This does undefined behavior if 2 is missing from the map, but if 2 is present it will evaluate to the same thing as m[2]. Alterantively, m.at(2) will again do the same thing if 2 is present as a key, and throw an exception if is not present.
As an aside, when working on such a problem, try breaking your code down into smaller pieces. m[2]; all by itself on a line will fail to compile. In fact, the error message told you that it could not find an operator[] -- that might have given you a clue what was wrong.
Your map m is declared const meaning that you can only call const functions on it.
std:map::operator[] is not a such function, since it will modify the map if the specified key is not found.
What you need is std::map::at, which is const so you can call it on your const map.

find function in c++ for a vector

Okay, so I am new to this, and I am having trouble understanding this.
I made this code,
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main (void)
{
vector <int> a;
int i;
a.push_back(1);
a.push_back(2);
a.push_back(3);
a.push_back(4);
a.push_back(5);
vector <int>::iterator position = find(a.begin(),a.end(),6);
cout<<*position<<"\n";
return 0;
}
I read everywhere that the find function if it doesnt find anything returns the iterator to the end of the vector. So, when, I do this, and search the value 6 which is not present in the vector, it should return the iterator to the end of the vector which is actually 5. On printing the actual value, it should then print 5 but it prints 0. Why is it that if it returns the iterator to the last value if find doesnt find anything in the vector relating to the value, then it prints 0?
No, it will not return the end of the vector it will return vector.end()
The former implies the last element of the vector, which would be 5.
The latter, vector.end(), is past the end of the vector
This adds slight confusion because vector.begin() does indeed point to the first element of the vector, but this is necessary due to the way you can iterate these containers.
Why is it that if it returns the iterator to the last value if find doesnt find anything in the vector relating to the value, then it prints 0
0 is a garbage value that you get when you dereference the end iterator of the vector. This is undefined behavior, so you could potentially get any number at all. A proper code looks like this:
vector <int>::iterator position = find(a.begin(),a.end(),6);
if (position != a.end()) {
cout<<*position<<"\n";
} else {
cout << "not found" << endl;
}
Once you get the result, you need to compare it to a.end() to see if the result is valid. If the result is not valid, you may not dereference the iterator to avoid undefined behavior.
As you can see in the documentation for vector::end(), the end() iterator is not the same as the last element in the vector. Rather, it is past the last element. Accessing that element results in undefined behavior.

C++ list iterator to integer conversion?

Hello guys I have a list iterator type which I want to convert to UINT.
std::set<UINT> volSet;
UINT ssidBase = 1;
UINT ssidEnd = 2;
volSet.insert( ssidBase );
volSet.insert (ssidEnd);
UINT ssidStart = evf::volSet.begin();
I want the first value in the volSet to be set to ssidStart. I'm getting an error when I run this?
cannot convert 'std::_Tree<std::_Tset_traits<unsigned int, std::less< unsigned int>, std::allocator< unsigned int>, false> >::iterator' to 'UINT' in initialization
Any help is appreciated?
As such, iterators in C++ are basically pointers.
So the general idea is that you need to dereference that iterator to access the actual value it points to, like so:
UINT number = *(container.begin());
It is not clear exactly what the intention is with inserting a one and then a two. Granted many more unsigned values can be inserted later. However, by inserting a one, the only other possible value that will be lower is zero. The insert method for the STL set, map, unordered_set, and unordered_map containers (now called Standard Library) returns a pair.
Insert method reference links:
set
http://www.cplusplus.com/reference/set/set/insert/
map
http://www.cplusplus.com/reference/map/map/insert/
unordered_set
http://www.cplusplus.com/reference/unordered_set/unordered_set/insert/
unordered_map
http://www.cplusplus.com/reference/unordered_map/unordered_map/insert/
If the element already exists in a set, the first pair element is the iterator pointing to the existing element with the second element set to false. With a successful insert (e.g. the key is not already present in the set), the first element of the pair points to the element that has been successfully added, with the second pair element set to true.
Using a set to guarantee the first (lowest) element (e.g. where the begin() method will always return an iterator pointing to unless the set is empty), does guarantee that the lowest value in the set is always found in constant time (e.g. O(1); big Oh of one).
A coding example with some values in C++11.
std::set<UINT> volSet;
pair <std::set<UINT>::itertor, bool> p;
//
// Using the same notation on unsigned int for consistency,
// insert 500 unsigned values into the volSet container.
//
for (UINT i = 500; i >= 0; i--)
{
p = volSet.insert(i);
if (p.second == false)
{
cerr << "volSet insertion warning!! The value for volSet already has: ";
cerr << i << endl;
}
}
//
// Do business logic, deletions/insertions from/to the volSet, and so on...
//
// Now obtain the lowest value inside the volSet and set it to a local variable.
// It is assumed that evf is a user defined namespace.
//
std::set<UINT>::iterator ii = evf::volSet.begin();
UINT ssidStart = *ii;
//
// Alternatively, implement without declaring a local iterator as Victor
// has shown above. Note that the parenthesis here are important. The call
// to begin() has to occur prior to the dereference operator call.
// Note that the sample above intentionally sets the ii iterator
// before dereferencing.
//
UNIT lowValue = *(evf::volSet.begin());
I hope this is helpful to understand the difference between iterators and container elements.

Order of Vector elements for C++

The following piece of c++ code gives
int main()
{
vector <int> myvect(3,0);
vector <int> :: iterator it;
it = myvect.begin();
myvect.insert(it,200);
myvect.insert(it+5,400); //Not sure what 5 makes the difference here
cout << myvect[0] << endl << myvect[1];
}
Output :
200
400
And the same code with minor changes gives
int main()
{
vector <int> myvect(3,0);
vector <int> :: iterator it;
it = myvect.begin();
myvect.insert(it,200);
myvect.insert(it+4,400); //Not sure what 4 makes the difference here
cout << myvect[0] << endl << myvect[1];
}
Output:
400
200
Can someone tell me why adding 4 or 5 to the iterator changes the order of elements?
Thanks
Your program has Undefined Behavior.
You are creating a vector of 3 elements (all initialized to 0), and you are inserting elements at position v.begin() + 5, which is beyond the end of the vector.
Moreover, you are using an iterator (it) after inserting an element before the position it points to. According to Paragraph 23.3.6.5/1 of the C++11 Standard:
[...] If no reallocation happens, all the iterators and references before the insertion point remain valid. [...]
Therefore, iterator it itself is not guaranteed to be valid after the statement myvect.insert(it, 200), and using it in the next instruction (myvect.insert(it + 4, 400)) is again Undefined Behavior.
You cannot expect anything of a program with Undefined Behavior. It may crash, give you bizarre results, or (in the worst case) behave just as you would expect.
The member function vector::insert(const_iterator, const value_type&) requires a valid iterator that refers to the vector but it+4 and it+5 are not valid iterators.
Before the first insertion, it+3 is a valid (non-dereferencable) iterator, pointing just past-the-end of the vector sequence, but it+4 is invalid. After the insertion it might get invalidated, in which case no expression using it is valid, certainly not it+5 because the sequence only has four elements at that point.
The code would be valid if changed like so:
it = myvect.begin();
myvect.insert(it,200);
it = myvect.begin(); // make it valid again
myvect.insert(it+4,400);