If we make a reference to a vector element and then resize the vector, the reference is no longer valid, the same happens with an iterator:
std::vector<int> vec{0, 1, 2, 3, 4, 5};
int& ref = vec[0];
auto itr = vec.begin();
cout << ref << " " << *itr << endl;
vec[0] = 7;
cout << ref << " " << *itr << endl;
vec.resize(100);
vec[0] = 3;
cout << ref << " " << *itr << endl;
Prints out:
0 0
7 7
0 0 // We expected a 3 here
And I know that it would be more practical to just keep a reference to the vector itself and call vec[0], but just for the sake of questioning, is it possible to keep an object that will always be vec[0] even if the object is moved?
I've tried writing a small helper class to help with this, but I'm unsure if this is the best method or if it can even fail?
template<typename T>
struct HelperClass
{
std::vector<T>& vec;
size_t element;
HelperClass(std::vector<T>& vec_, size_t element_) : vec(vec_) , element(element_) {}
// Either define an implicit conversion from HelperClass to T
// or a 'dereference' operator that returns vec[0]
operator T&() { return vec.at(element); }
T& operator*() { return vec.at(element); }
};
And use it by either the implicit conversion to T& or by the 'dereference' operator:
std::vector<int> vec{0, 1, 2, 3, 4, 5};
int& ref = vec[0];
auto itr = vec.begin();
HelperClass<int> hlp = HelperClass<int>(vec, 0); // HelperClass
cout << ref << " " << *itr << " " << hlp << " " << *hlp << endl;
vec[0] = 7;
cout << ref << " " << *itr << " " << hlp << " " << *hlp << endl;
vec.resize(100);
vec[0] = 3;
cout << ref << " " << *itr << " " << hlp << " " << *hlp << endl;
Which already prints what was excepted:
0 0 0 0
7 7 7 7
0 0 3 3
So is there a better way to do this aside from having a helper class and can the helper class be unreliable in some cases?
I've also come across this thread in reddit, but it seems that they do not discuss the helper class there
The one thing you could do is have a vector of pointers rather than a vector of instances. That of course has its own passel of issues but if you must have object references survive a vector resize that will do it.
Any reallocation of the vector will invalidate any pointers, references and iterators.
In your example, your HelperClass is useless in sense that this:
cout << ref << " " << *itr << " " << hlp << " " << *hlp << endl;
is the same as:
cout << ref << " " << *itr << " " << vec[0] << " " << vec[0] << endl;
If a reallocation happens, just use the iterator interface .begin() .end() to access again the iterators.
Related
I wrote this simple c++ program and I got some strange results that I don't understand (results are described in the line comments)
int arr[3] {1, 2, 3};
int* p{ nullptr };
p = arr;
std::cout << p[0] << " " << p[1] << " " << p[2]; // prints 1 2 3, OK
p = arr;
std::cout << *(p++) << " " << *(p++) << " " << *(p); // prints 2 1 3 ??
p = arr;
std::cout << *p << " " << *(++p) << " " << *(++p); // prints 3 3 3 ??
p = arr;
std::cout << *p << " "; ++p;
std::cout << *p << " "; ++p;
std::cout << *p; // prints 1 2 3, OK
it seems that the pointer increments along a std::cout concatenation don't work.
What's wrong in my idea?
I supposed it should have worked.
best
final edit: I was using c++14, I switched to c++20 and now it works properly
thank you everybody!
int* p{ nullptr };
std::cout << p[0] << " " << p[1] << " " << p[2];
This is Undefined Behavior, as you are dereferencing nullptr, p does not point at valid memory yet.
p = arr;
std::cout << p[0] << " " << p[1] << " " << p[2];
This is well-defined behavior. p points at valid memory, is always incremented before dereferenced, and is incremented in a deterministic and valid manner. This is the same as if you had written the following instead:
std::cout << *(p+0) << " " << *(p+1) << " " << *(p+2);
p = arr;
std::cout << *(p++) << " " << *(p++) << " " << *(p);
p = arr;
std::cout << *p << " " << *(++p) << " " << *(++p);
Both of these are Undefined Behavior prior to C++17, because the order in which chained operator<< calls are evaluated is not guaranteed in earlier versions, the compiler is free to evaluate them in whatever order it wants. This is no longer the case in C++17 onward.
p = arr;
std::cout << *p << " "; ++p;
std::cout << *p << " "; ++p;
std::cout << *p;
This is well-defined behavior. p points at valid memory, is always dereferenced before incremented, and is incremented in a deterministic and valid manner.
I am trying to understand std::find(). Below is my code.
std::set::find searches the container for an element equivalent to
val and returns an iterator to it if found, otherwise it returns an
iterator to set::end.
But when I gave find(100) I am getting 7 rather than 20.
#include <iostream>
#include <set>
using namespace std;
int main()
{
set <int> s1{20, 7, 2};
s1.insert(10);
s1.insert(5);
s1.insert(15);
s1.insert(1);
cout << "size() : " << s1.size() << endl;
cout << "max_size() : " << s1.max_size() << endl;
cout << "empty() : " << s1.empty() << endl;
for(auto itr = s1.begin(); itr != s1.end(); itr++)
cout << *itr << " ";
cout << endl;
cout << endl << "---- find(value) ----" << endl;
auto a1 = s1.find(10);
//cout << "find(10) : " << a1 << " " << *a1 << endl;
cout << "find(10) : " << *a1 << endl;
auto a2 = s1.find(100);
cout << "find(100) : " << *a2 << endl;
cout << endl << "---- count(value) ----" << endl;
cout << "s1.count(10) : " << s1.count(10) << endl;
cout << "s1.count(100) : " << s1.count(100) << endl;
return 0;
}
Output:
size() : 7
max_size() : 107374182
empty() : 0
1 2 5 7 10 15 20
---- find(value) ----
find(10) : 10
find(100) : 7
---- count(value) ----
s1.count(10) : 1
s1.count(100) : 0
The problem is that you're dereferencing an iterator a2 that points to s1.end() leading to undefined behavior. This problem arose because you're not checking before dereferencing the iterators, if the element was found or not.
To solve this you should add an explicit check before dereferencing the iterators.
//dereference only if the element was found
if(a2!=s1.end())
{
std::cout << "find(100) : " << *a2 << std::endl;
}
//otherwise print a message saying element not found
else
{
std::cout<<"element not found"<<std::endl;
}
auto a2 = s1.find(100);
cout << "find(100) : " << *a2 << endl;
Here you dereference (*a2) the end iterator. That is undefined behaviour - remember that s1.end() points to one past the last element and must not be dereferenced.
You're unlucky that you got a value from that dereference - it would be more convenient if your program crashed or otherwise reported the problem. But UB doesn't have to be diagnosed in any way.
You might have spotted the problem if you had run your program using Valgrind's memory checker (or your preferred equivalent). But there's a good chance that's unable to detect it (if the set has over-allocated, which is likely).
The value 100 is not present in the set. So this call
auto a2 = s1.find(100);
returns the iterator s1.end(). You may not dereference the iterator. This statement
cout << "find(100) : " << *a2 << endl;
invokes undefined behavior.
#include<bits/stdc++.h>
using namespace std;
int main() {
vector <int> v = {1, 2 , 3, 2 , 1};
cout << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << " " << v[4] << endl;
sort(v.begin(), v.end());
cout << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << " " << v[4] << endl;
unique(v.begin(), v.end());
cout << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << " " << v[4] << endl;
}
Output is
1 2 3 2 1
1 1 2 2 3
1 2 3 2 3
I'm not understanding what is unique function and how it is working ??
Aside the fact, that bits/stdc++.h is not the proper header when taking C++ standard into account (please use iostream, vector and algorithm).
From: https://en.cppreference.com/w/cpp/algorithm/unique
Eliminates all except the first element from every consecutive group of equivalent elements from the range [first, last) and returns a past-the-end iterator for the new logical end of the range.
Removing is done by shifting the elements in the range in such a way that elements to be erased are overwritten
Thus the end of the vector might contain "garbage", but this is fine, as the new end of it is also returned.
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
vector <int> v = {1, 2 , 3, 2 , 1};
cout << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << " " << v[4] << endl;
sort(v.begin(), v.end());
cout << v[0] << " " << v[1] << " " << v[2] << " " << v[3] << " " << v[4] << endl;
auto new_end = unique(v.begin(), v.end());
auto b = v.begin();
while (b!=new_end) {
std::cout << *b++ << " ";
}
std::cout << "\n";
return 0;
}
demo
I was playing around with pointers and got results I did not expect:
#include <iostream>
#include <vector>
int main() {
int arr[4] = { 1, 2, 3, 4 };
int* pArr = arr;
std::cout << "First number: " << *pArr << " at address: " << pArr;
pArr++;
std::cout << "\nSecond number: " << *pArr << " at address: " << pArr;
pArr++;
std::cout << "\nThird number: " << *pArr << " at address: " << pArr;
pArr++;
std::cout << "\nFourth number: " << *pArr << " at address: " << pArr;
int* pArr2 = arr;
std::cout << "\n"
<< *pArr2++ << "\n"
<< *pArr2++ << "\n"
<< *pArr2++ << "\n"
<< *pArr2++ << "\n";
/*
int* pArr2 = arr;
std::cout << "\n"
<< ++ * pArr2 << "\n"
<< * ++pArr2 << "\n";
*/
}
The two different results:
1 2 3 4 - as expected using the first method
4 3 2 1 - using cout with multiple arguments I do not know the proper name.
So my question is - why does this happen? Using multiple cout statements results in expected for me code, while using just 1 cout results in backwards solution.
As a side note, another confusing thing to me is that pre-increment results in all values being equal. In the commented bit of code, the result is 3 3, no matter the ++ placement with respect to the *.
This code:
std::cout << "\n" << *pArr2++ << "\n";
std::cout << "\n" << *pArr2++ << "\n";
has a well defined order of modifications to pArr and will print
1
2
But this code:
std::cout << "\n" << *pArr2++ << "\n" << *pArr2++ << "\n";
invokes undefined behavior before c++17, because there are multiple modifications to pArr2 that are unsequenced. The program has UB, so it could print anything.
From c++17, there is a sequence point between the modifications, and the above code is guaranteed to print:
1
2
The code below shows a multi_index container which is indexed by sequence and order.
In my use case elements will be mainly searched by index, and if existing, the next element (by order) is obtained.
My question is, how to get the rank (by order) of obtained next element?
#include <iostream>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/ranked_index.hpp>
#include <boost/multi_index/identity.hpp>
using namespace boost::multi_index;
typedef multi_index_container <
int
, indexed_by<
sequenced<>
, ordered_non_unique<identity<int>>
, ranked_non_unique<identity<int>>
>
> Ints;
int main() {
Ints ints;
auto & sequence=ints.get<0>();
auto & order=ints.get<1>();
sequence.push_back(2);
sequence.push_back(-1);
sequence.push_back(5);
sequence.push_back(6);
auto it = order.find(2);
if (it!=order.end()) {
std::cout
<< "next to "
<< *it
<< " by sequence is "
<< *(++(ints.project<0>(it)))
<< std::endl
;
std::cout
<< "next to "
<< *it
<< " by order is "
<< *(++(ints.project<1>(it))) //++it is good too
<< std::endl
;
std::cout
<< "rank of next by sequence is "
// << ??? ints.rank<???>(???)
<< std::endl
;
std::cout
<< "rank of next by order is "
// << ??? ints.rank<???>(???)
<< std::endl
;
}
}
#sehe's answer is perfectly valid but runs in linear time. If you want better performance, consider defining your index #0 as random_access and #1 as ranked_non_unique (index #2 is redundant):
typedef multi_index_container <
int
, indexed_by<
random_access<>
, ranked_non_unique<identity<int>>
>
> Ints;
so that you can write:
std::cout
<< "rank of next by sequence is "
<< ints.project<0>(it)-sequence.begin()+1 // O(1)
<< std::endl
;
std::cout
<< "rank of next by order is "
<< order.rank(it)+1 // O(log n)
<< std::endl
;
Assuming you want some kind of "index into" or "offset from begin" in the sequenced index:
if (it!=order.end()) {
auto rank_of = [&](auto it) {
return std::distance(sequence.begin(), ints.project<0>(it));
};
auto seq_next = std::next(seq_it);
auto ord_next = std::next(it);
if (seq_next!=sequence.end())
{
std::cout << "next to " << *it << " by sequence is " << *seq_next << std::endl;
std::cout << "rank of next by sequence is " << rank_of(seq_next) << std::endl;
}
if (ord_next!=order.end())
{
std::cout << "next to " << *it << " by order is " << *ord_next << std::endl ;
std::cout << "rank of next by order is " << rank_of(ord_next) << std::endl;
}
}
Without polymorphic lambdas you should write it out