I have used a decent amount of C++, but not so much std::list ..
In my current project I need a std::list<..> data member, as well as keep track to a position in the list with a std::list<..>::iterator. The object must also be movable, but a default move constructor is not possible in my case. Here std::list does something that surprises me.
Consider
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void test() {
T l { 1, 2, 3, 4, 5 };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
auto pos = find(l.begin(), l.end(), 6);
if (pos == l.end()) cout << "l end\n";
cout << "---- moving l > lmv ----" << endl;
T lmv { std::move(l) };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
cout << "lmv = "; for(const auto& e: lmv) cout << e << " "; cout << endl;
if (pos == l.end()) cout << "l end\n";
if (pos == lmv.end()) cout << "lmv end\n";
}
int main() {
cout << "___vector___\n";
test<vector<int>>();
cout << "___list___\n";
test<list<int>>();
}
This outputs
___vector___
l = 1 2 3 4 5
l end
---- moving l > lmv ----
l =
lmv = 1 2 3 4 5
lmv end
___list___
l = 1 2 3 4 5
l end
---- moving l > lmv ----
l =
lmv = 1 2 3 4 5
l end
I.e. the iterator that pointed to the moved-from lists end, does not point to the moved-to lists end.
But it does for vector, which is what I would always expect, if iterators are essentially pointers. Why is list different? Memory location of elements should not change with move .. does lists move change list iterators? Why?
I am using "g++.exe (Rev1, Built by MSYS2 project) 10.2.0"
under MSYS2 on Windows 10
Iterators should be preserved when moving a container.
However end iterators of a container don't point to an element and are therefore allowed to be invalidated when moving a container.
If you change your code to work with begin rather than end then it works as you expect.
#include <list>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
template<typename T>
void test() {
T l { 1, 2, 3, 4, 5 };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
auto pos = find(l.begin(), l.end(), 1);
if (pos == l.begin()) cout << "l begin\n";
cout << "---- moving l > lmv ----" << endl;
T lmv { std::move(l) };
cout << "l = "; for(const auto& e: l) cout << e << " "; cout << endl;
cout << "lmv = "; for(const auto& e: lmv) cout << e << " "; cout << endl;
if (pos == l.begin()) cout << "l begin\n";
if (pos == lmv.begin()) cout << "lmv begin\n";
}
int main() {
cout << "___vector___\n";
test<vector<int>>();
cout << "___list___\n";
test<list<int>>();
}
Note that comparing the iterators from two different containers is undefined behaviour so the final pos == l.begin() is undefined behaviour and visual studio's debug builds at least will throw assertions when running this code.
I imagine your original code works because the std::vector end iterator is usually just implemented as pointing to one after the last element. I would imagine the std::list end iterator holds a null pointer and a pointer to the list.
If you add such horrible lines at the end of your test function
(this is totally incorrect, the sanitizer will insult you!),
you can see that in the case of a vector the end() iterator
designates something which is past-the-end of the buffer containing
the stored elements, but in the case of a list the end iterator
designates some kind of marker which is stored inside the list
structure itself.
Then, after moving, the buffer of the vector is still the same
but it does not belong to l anymore, so the address past-the-end
of this buffer is equivalent to end() for lmv.
On the other hand, after moving the list, pos which designated
an address inside l still designated the same address (although
l is moved from) but does not designate the end() marker inside
lvm which didn't even exist when pos was initialised.
std::cout << "pos: " << (void *)(&*pos) << '\n';
std::cout << "l: " << (void *)(&l) << '\n';
std::cout << "l.begin(): " << (void *)(&*l.begin()) << '\n';
std::cout << "l.end(): " << (void *)(&*l.end()) << '\n';
std::cout << "lmv: " << (void *)(&lmv) << '\n';
std::cout << "lmv.begin(): " << (void *)(&*lmv.begin()) << '\n';
std::cout << "lmv.end(): " << (void *)(&*lmv.end()) << '\n';
Related
In my std::list, i have 10 elements, and i want to move the number 1000 to the back of the list.
https://leetcode.com/playground/gucNuPit
is there a better way, a 1 liner using std::move, back inserter or any other C++ syntax to achieve this with consciously?
// Move the number 1000 to the end of the list
#include <iostream>
#include <algorithm>
#include <list>
using namespace std;
int main() {
list<int> myList({2,3,4,1000,5,6,7,8,9,10});
cout << "List before " << endl;
for(auto e : myList)
cout << e << " ";
// get iterator to the number 1000 in the list
list<int>::iterator findIter = std::find(myList.begin(), myList.end(), 1000);
int val_to_move_to_end = *findIter;
myList.erase(findIter);
myList.push_back(val_to_move_to_end);
cout << endl << endl << "List after " << endl;
for(auto e : myList)
cout << e << " ";
return 0;
}
You can use the std::list::splice(..) to achieve this
myList.splice(myList.end(), myList, std::find(myList.begin(), myList.end(), 1000));
Check out the documentation
In the following code, I print the first element of a vector and its size, before and after calling clear( ) method of the in-built vector.
However, even after calling clear( ), I can still refer to the element of the vector( although the size is reset to '0').
Is this expected behaviour or have I not understood the behaviour of clear( ) well?
CODE:
#include <vector>
#include <iostream>
using namespace std;
int main(int argc, char *argv[]){
vector<int> vec = {1, 2, 3, 4, 5};
cout << vec[0] << " " << vec.size() << endl;
vec.clear();
for(auto i : vec)
cout << i << " ";
cout << endl;
cout << vec[0] << " " << vec.size() << endl;
return 0;
}
OUTPUT:
1 5
1 0
The indexing operator of std::vector<> does not check the index. Since it's Undefined Behavior, you might get "what was there before", or it might crash (or "whatever").
You have the at member function, which will throw if index is out of range. That is:
cout << vec.at(0) << " " << vec.size() << endl;
near the end of your main() will throw.
I'm getting problem with segmentation fault when trying to compile a C++ program, but not sure where the problem lies. I suspect that the problem lies with the .find() ..... could it be the iterator operator < and == which are the comparators for find() that is the issue? I hope that someone can point out to me where they think the problem lies.
The following is part of test01.cpp, where I run it to test the code and use print statements to find out where the problem is:
bool confirmEverythingMatches(const btree<long>& testContainer, const set<long>& stableContainer) {
cout << "Confirms the btree and the set "
"contain exactly the same values..." << endl;
for (long i = kMinInteger; i <= kMaxInteger; i++) {
cout << "Start of for-loop to find iterator for comparisons..." << endl;
if (stableContainer.find(i) != stableContainer.end()) {
cout << "can find i (" << i << ") in stableContainer!" << endl;
} else {
cout << "cannot find i (" << i << ") in stableContainer!" << endl;
}
cout << "In between finding i in stable and testContainers..." << endl;
if (testContainer.find(i) != testContainer.end()) {
cout << "can find i (" << i << ") in testContainer!" << endl;
} else {
cout << "cannot find i (" << i << ") in testContainer!" << endl;
}
cout << "Before assigning the find to boolean variables..." << endl;
bool foundInTree = (testContainer.find(i) != testContainer.end());
cout << "testContainer.find(i) != testContainer.end()" << endl;
bool foundInSet = (stableContainer.find(i) != stableContainer.end());
cout << "stableContainer.find(i) != stableContainer.end()" << endl;
if (foundInTree != foundInSet) {
cout << "- btree and set don't contain the same data!" << endl;
cout << "Mismatch at element: " << i << endl;
return false;
} else {cout << "foundInTree == foundInSet!!!" << i << endl;}
}
cout << "- btree checks out just fine." << endl;
return true;
}
} // namespace close
/**
* Codes for testing various bits and pieces. Most of the code is commented out
* you should uncomment it as appropriate.
**/
int main(void) {
// initialise random number generator with 'random' seed
initRandom();
cout << "after initRandom().." << endl;
// insert lots of random numbers and compare with a known correct container
btree<long> testContainer(99);
cout << "after specifying max node elements in testContainer.." << endl;
set<long> stableContainer;
cout << "after constructing stableContainer.." << endl;
insertRandomNumbers(testContainer, stableContainer, 1000000);
cout << "after inserting random numbers into testContainer and for success inserts, also into stableContainer.." << endl;
btree<long> btcpy = testContainer;
cout << "after copy assigning a copy of testContainer to btcopy.." << endl;
confirmEverythingMatches(btcpy, stableContainer);
cout << "after confirming everything internally matches between testContainer and stableContainer.." << endl;
return 0;
}
The output I get when running the program (No problem when compiling) is this:
Confirms the btree and the set contain exactly the same values...
Start of for-loop to find iterator for comparisons...
cannot find i (1000000) in stableContainer!
In between finding i in stable and testContainers...
ASAN:DEADLYSIGNAL
=================================================================
==7345==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000018 (pc 0x000108d132a8 bp 0x000000000000 sp 0x7fff56eee6f0 T0)
#0 0x108d132a7 in btree<long>::find(long const&) const (test01+0x1000022a7)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (test01+0x1000022a7) in btree<long>::find(long const&) const
==7345==ABORTING
Abort trap: 6
I also got this error when I tried to run it on another machine:
==29936==ERROR: AddressSanitizer failed to allocate 0x200000 (2097152) bytes of SizeClassAllocator32: 12
I found that when it goes into the find(), it will have the segmentation fault:
/**
* Identical in functionality to the non-const version of find,
* save the fact that what's pointed to by the returned iterator
* is deemed as const and immutable.
*
* #param elem the client element we are trying to match.
* #return an iterator to the matching element, or whatever the
* const end() returns if no such match was ever found.
*/
template<typename T> typename btree<T>::const_iterator
btree<T>::find(const T& elem) const {
std::cout << "CONST ITERATOR'S FIND" << std::endl;
Node *tmp_ = root_;
std::cout << "1" << std::endl;
while(true) {
std::cout << "2" << std::endl;
size_t i;
std::cout << "3" << std::endl;
// go through all elements from root to tail
for (i = 0; i < tmp_->__occupied_size_; ++i) {
std::cout << "4" << std::endl;
if (tmp_->__elem_[i] == elem) {
std::cout << "5" << std::endl;
// find the elem, return an iterator
return const_iterator(tmp_, i, this);
std::cout << "6" << std::endl;
} else if (tmp_->__elem_[i] > elem) {
std::cout << "7" << std::endl;
// elem is not in current Node, go to descendants
// for the elem.
if (tmp_->__descendants_ == nullptr) {
std::cout << "8" << std::endl;
return cend();
std::cout << "9" << std::endl;
} else {
std::cout << "10" << std::endl;
tmp_ = tmp_->__descendants_[i];
std::cout << "11" << std::endl;
break;
}
}
}
// handling boundaries cases
if (i == tmp_->__occupied_size_) {
std::cout << "12" << std::endl;
if (tmp_->__descendants_[i] == nullptr) {
std::cout << "13" << std::endl;
return cend();
std::cout << "14" << std::endl;
} else {
std::cout << "15" << std::endl;
tmp_ = tmp_->__descendants_[i];
}
}
}
}
The print statements for this find is:
CONST ITERATOR'S FIND
1
2
3
4
4
7
10
11
2
3
4
7
10
11
ASAN:DEADLYSIGNAL
Ok, so based on the implementation of this find function, I think the problem might be located in
if (tmp_->__descendants_ == nullptr) {
std::cout << "8" << std::endl;
return cend();
std::cout << "9" << std::endl;
} else {
std::cout << "10" << std::endl;
tmp_ = tmp_->__descendants_[i];
std::cout << "11" << std::endl;
break;
}
and then
// handling boundaries cases
if (i == tmp_->__occupied_size_) {
std::cout << "12" << std::endl;
if (tmp_->__descendants_[i] == nullptr) {
std::cout << "13" << std::endl;
return cend();
std::cout << "14" << std::endl;
} else {
std::cout << "15" << std::endl;
tmp_ = tmp_->__descendants_[i];
}
}
So, You are checking if tmp->__descendants_ is not null. If it is not, then you set tmp_ = tmp_->descendants_[i];
Note: you are just checking __descendants_ pointer to be null or not, you are not checking the __descendants_ [i] if it is null!
What if the tmp->__descendants_[i] is null (or gets out of the descendants array)?
If that value is null, then tmp_->occupied_size_ might give you segfault.
Note2: For some reason you are using same index "i" for iterating through __elem_ and __descendants_. I am not sure, how descendants are created, but it might be also a problem here.
This is why debuggers exist. Run your program in the debugger, let the program fail, and then the debugger will tell you where and why it's gone wrong.
It looks like you've got potentially a lot of code to trawl through, which no one here will really want to do as it's not a concise question.
Good luck!
In this sample program, why the value of the data pointed by the iterator is kept, even after the list is empty?
Is it something bound to happen due to the implementation of iterators in C++ (i.e. the value of the object is kept into the iterator) or is it because the segment of the memory was declared as free for used, but hasn't been changed yet?
#include <iostream>
#include <list>
using namespace std;
int main ()
{
list<int> mylist;
list<int>::iterator it = mylist.begin();
cout << "printing: " << *it << endl;
mylist.push_back(77);
mylist.push_back(22);
// now front equals 77, and back 22
mylist.front() -= mylist.back();
it = mylist.begin();
cout << "printing: " << *it << endl;
cout << "mylist.front() is now " << mylist.front() << '\n';
// trying to delete all elements and then see how the iterators are handling it
it = mylist.begin();
cout << "printing: " << *it << endl;
mylist.remove(55);
cout << "printing: " << *it << endl;
mylist.remove(22);
cout << "printing: " << *it << endl;
cout << "mylist size: " << mylist.size() << endl;
cout << "mylist.front() is now " << mylist.front() << '\n';
cout << "printing: " << *it << endl;
return 0;
}
And this is the output:
printing: 495034304
printing: 55
mylist.front() is now 55
printing: 55
printing: 55
printing: 55
mylist size: 0
mylist.front() is now 38375440
printing: 55
Is it something bound to happen due to the implementation of iterators in C++
No, it's undefined behaviour. The iterator has become invalid, and can't be used.
is it because the segment of the memory was declared as free for used, but hasn't been changed yet?
Yes, that's why you observed what you observed. But the memory could be reused for something else, or made inaccessible - you can't rely on any observations of undefined behaviour.
Iterator is invalidated by you operations, but it may still point to memory with the previous value. Anyway, accessing it after the value is removed from the list is undefined behaviour.
#include <stdio.h>
int main(int argc, char **argv)
{
int *p = NULL;
p = (int*)malloc(sizeof(int));
*p = 5;
printf("P: %d\n", *p);
free(p);
printf("P: %d\n", *p);
}
Why is this still a surprise? Marking a pointer as invalid has nothing to do with what is stored where it used to point.
I have a map having strings as keys and lists of file names as values.
ex: Map(firstDir, list(file1,file2,file3))
I know that by using following code I can print value which is of String
{
cout << "Key: " << pos->first << endl;
cout << "Value:" << pos->second << endl;
}
But if pos->second contains a List, how to display?
overload operator << for list
std::ostream& operator << (std::ostream& out, std::list<ListElemType> const& lst)
{
for(std::list<ListElemType>::iterator it = lst.begin(); it != lst.end(); ++it)
{
if(it != lst.begin())
out << /*your delimiter*/;
out << *it;
}
return out;
}
now you can do what you want
cout << "Key: " << pos->first << endl << "Value:" << pos->second << endl;
How about using Boost.Foreach ?
#include <boost/foreach.hpp>
{
cout << "Key: " << pos->first << endl;
cout << "Values:" << endl;
BOOST_FOREACH(std::string const& file, pos->second)
{
cout << "\t" << file << endl;
}
}
The first thing you would have to decide is how do you want the list to be displayed? Separated by commas or each entry in a new line perhaps? Then, you could overload the stream output operator for lists of strings:
std::ostream & operator<<(std::ostream & stream, const std::list<std::string> & object) {
std::copy(object.begin(), object.end(), std::ostream_iterator<std::string>(std::cout, ", ")
}
This operator will get called everytime you write am std::list<std::string> to any output stream, and it will print the values of the list separated by commas.
Use a library which overloads stream inserters for containers, such as my example:
void example(MapType const &m) {
using namespace kniht::container_inserters; // must be enabled in this scope
MapType::const_iterator x = m.begin();
cout << *x << '\n'; // can print the pair directly
cout << "Key: " << x->first << '\n'; // or format it yourself
cout << "Value: " << x->second << '\n';
// output for a list: [a, b, c]
}
You can extract the used functions from my header or simply copy it elsewere (it's self-contained, but does have other utilities).
Since you are using STL the most simple way to print such structure is the next one:
#include <iostream>
#include <map>
#include <list>
#include <string>
using namespace std;
int main()
{
map<string, list<string>> sample;
... //fill the map
for (auto itr : sample){
cout << itr.first << ":\t" << endl;
for (auto innerItr : sample.second)
cout << innerItr << " ";
cout << endl;
}
}
What you want to be able to do though is to create a template that can output any printable sequence, i.e. a sequence of printable items. You also want to be able to customise how you start, end and delimit the sequence.
So create your own wrapper class. Now because it is your wrapper you can overload operator<< happily on it without interfering with an external namespace.
You could use a boost::range in its constructor, or a templated being/end sequence or a templated collection.
Also take as parameters your delimiters. You can have reasonable defaults if you want though,
Finally when you wish to print one, you just do something like:
std::cout << MySequenceFormatter( seq.begin(), seq.end(), delim, startofSeq, endOfSeq ) << std::endl;
When outputting maps, you can use a boost::transform_iterator in between to transform each std::pair as you iterate through the sequence. Then you can have you own pair formatter too.
In fact you can do this for any sequence where you wish to use a custom method to print the items themselves.
Strait forward version (-:
#include <map>
#include <list>
#include <string>
#include <iostream>
#include <sstream> // only for generating testdata
typedef std::list<std::string> TStringList;
typedef std::map<std::string, TStringList> TStringListMap;
TStringListMap myMap;
int main()
{
// Generating testdata
for (int i=0;i<10;i++)
{
std::stringstream kstr;
kstr << i;
std::string key = kstr.str();
for (int ii=0;ii<=i;ii++)
{
std::stringstream vstr;
vstr << ii;
myMap[key].push_back( vstr.str() );
}
}
//Print map
for ( TStringListMap::const_iterator it = myMap.begin(), end = myMap.end(); it != end; ++it )
{
std::cout << it->first<< ": ";
for( TStringList::const_iterator lit = it->second.begin(), lend = it->second.end(); lit != lend; ++lit )
{
std::cout << *lit << " ";
}
std::cout << std::endl;
}
return 0;
}
Output:
0: 0
1: 0 1
2: 0 1 2
3: 0 1 2 3
4: 0 1 2 3 4
5: 0 1 2 3 4 5
6: 0 1 2 3 4 5 6
7: 0 1 2 3 4 5 6 7
8: 0 1 2 3 4 5 6 7 8
9: 0 1 2 3 4 5 6 7 8 9