I have a double dimension std::string array which I need to pass as argument of function sortString but I get a runtime error when variable student is first read. "0" is output via cout, but not "1". Any idea of where I am wrong?
Here is my code:
#include <iostream>
#include <string>
void sortString(std::string **student, std::string **output, int size)
{
std::string names[5];
std::cout << "0" << std::endl;
for (int i = 0 ; i < size ; i++)
names[i] = student[i][0];
std::cout << "1" << std::endl;
}
int main()
{
std::string student1 [ ] = {"Joe Lime", "15", "2019"};
std::string student2 [ ] = {"Bob Green", "3", "2020"};
std::string student3 [ ] = {"SallyAnne Green" , "1", "2017"};
std::string student4 [ ] = {"Annie Blue", "10", "2020"};
std::string student5 [ ] = {"Jose Lemon", "25", "2016"};
int const size = 5;
std::string student [5][3] = {student1, student2, student3, student4, student5};
std::string sortedByName[5][3];
sortString((std::string**)student, (std::string**)sortedByName, size);
return 0;
}
** ------------ EDIT ------------ **
I wanted to do the same thing as I do for unidimensional arrays, so I don't understand why it doesn't work for 2-dimensional arrays
e.g, this works :
#include <iostream>
int test(int *a)
{
std::cout << a[0] << std::endl;
}
int main()
{
int a[] = {1,2,3,4,5,6,7};
test(a);
}
int test(int *a)
{
std::cout << a[0] << std::endl;
}
You have a big confusion with arrays, pointers and strings. As #Quentin and
#molbdnilo pointed you out, you are doing a C-style conversion from a bidimensional array of std::strings to a pointer to a pointer to a string, and neither arrays are pointers nor pointers are arrays.
My guess is that you want to sort all the students according to their name, while keeping the rest of the student information associated to the corresponding student.
A couple of advices:
Do not use C-style arrays whenever you can use std::array.
To define the constants in your code, create a constant variable, do not write, for example, 5 as size, this can involve multiple changes in different parts of your code when you want to change that constant value, since it can be written in multiple locations.
You don't need to use pointers in your example. They don't make sense in this scenario.
An example of what you are trying to achieve that uses the std::sort function:
#include <string>
#include <array>
#include <iostream>
#include <algorithm>
const unsigned NUMBER_OF_STUDENTS = 5;
const unsigned NUMBER_OF_STUDENT_DATA_FIELDS = 3;
using studentType = std::array<std::string, NUMBER_OF_STUDENT_DATA_FIELDS>;
int main()
{
std::array<std::string, NUMBER_OF_STUDENT_DATA_FIELDS> student1 = {"Joe Lime", "15", "2019"};
std::array<std::string, NUMBER_OF_STUDENT_DATA_FIELDS> student2 = {"Bob Green", "3", "2020"};
std::array<std::string, NUMBER_OF_STUDENT_DATA_FIELDS> student3 = {"SallyAnne Green" , "1", "2017"};
std::array<std::string, NUMBER_OF_STUDENT_DATA_FIELDS> student4 = {"Annie Blue", "10", "2020"};
std::array<std::string, NUMBER_OF_STUDENT_DATA_FIELDS> student5 = {"Jose Lemon", "25", "2016"};
std::array<studentType, NUMBER_OF_STUDENTS> students = {student1, student2, student3, student4, student5};
std::sort(students.begin(), students.end(), [](const studentType& student1, const studentType& student2) {
// first string in the array is the name
const std::string& nameStudent1 = student1.front();
const std::string& nameStudent2 = student2.front();
// we return if the name of student 1 is lexicographically smaller than the name of student 2
return nameStudent1 < nameStudent2;
});
// Let's print the students to see we have order everything correctly
for (const auto& student: students) // for each student
{
for (const auto& studentData : student) // for each field in the student string
{
std::cout << studentData << " ";
}
std::cout << "\n"; // jump to the next line
}
}
Annie Blue 10 2020
Bob Green 3 2020
Joe Lime 15 2019
Jose Lemon 25 2016
SallyAnne Green 1 2017
i fixed your code a bit and got it to work:
#include <iostream>
#include <string>
void sortString(std::string student[][3], std::string output[][3], int size)
{
std::string names[5];
std::cout << "0" << std::endl;
for (int i = 0; i < size; i++)
{
names[i] = student[i][0];
std::cout << names[i] << "\n";
}
std::cout << "1" << std::endl;
}
int main()
{
int const size = 5;
std::string students[5][3] =
{
{ "Joe Lime", "15", "2019" },
{ "Bob Green", "3", "2020" },
{ "SallyAnne Green", "1", "2017" },
{ "Annie Blue", "10", "2020" },
{ "Jose Lemon", "25", "2016" }
};
std::string sortedByName[5][3];
sortString(students, sortedByName, size);
return 0;
}
but i highly recommend you use arrays, vectors and structs/classes. following a made up an example with vector and arrays and vector and structs
#include <iostream>
#include <string>
#include <vector>
#include <array>
#include <algorithm>
void sortString(std::vector<std::array<std::string, 3>>& students)
{
// for example: print all names with range base for loop
for (const auto& s : students)
{
std::cout << s[0] << std::endl;
}
// for example: print all names with "normal" for loop
for (std::size_t i = 0; i < students.size(); ++i)
{
std::cout << students[i][0] << std::endl;
}
// sort by name
std::sort(std::begin(students), std::end(students), [](const std::array<std::string, 3>& a, const std::array<std::string, 3>& b){ return a[0] < b[0]; });
}
int main()
{
int const size = 5;
std::vector<std::array<std::string, 3>> students;
students.push_back({ "Joe Lime", "15", "2019" });
students.push_back({ "Bob Green", "3", "2020" });
students.push_back({ "SallyAnne Green", "1", "2017" });
students.push_back({ "SallyAnne Green", "1", "2017" });
students.push_back({ "Jose Lemon", "25", "2016" });
sortString(students);
return 0;
}
with struct:
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
struct Student
{
std::string name;
std::string dontKnow;
std::string year;
};
void sortString(std::vector<Student>& students)
{
// for example: print all names with range base for loop
for (const auto& s : students)
{
std::cout << s.name << std::endl;
}
// for example: print all names with "normal" for loop
for (std::size_t i = 0; i < students.size(); ++i)
{
std::cout << students[i].name << std::endl;
}
// sort by name
std::sort(std::begin(students), std::end(students), [](const Student& a, const Student& b){ return a.name < b.name; });
}
int main()
{
int const size = 5;
std::vector<Student> students;
students.push_back({ "Joe Lime", "15", "2019" });
students.push_back({ "Bob Green", "3", "2020" });
students.push_back({ "SallyAnne Green", "1", "2017" });
students.push_back({ "SallyAnne Green", "1", "2017" });
students.push_back({ "Jose Lemon", "25", "2016" });
sortString(students);
return 0;
}
i hope you see how much cleaner your code gets
Related
Here is the code I have, I am having trouble finding a way to remove all Apple elem in the array. I am able to count the apples in the array. I hope someone can help...
string items[10] = { "Apple", "Oranges", "Pears", "Apple", "bananas", "Apple", "Cucumbers", "Apple", "Lemons", "Apple" };
//Counts the total amount of apples
int n = sizeof(items) / sizeof(items[0]);
cout << "Number of times Apple appears : "
<< count(items, items + n, "Apple");
//remove the element Apple from array
if (string items[].contains("Apple"))
{
items[].remove("Apple");
}
Some options you'd have would be:
Walk your array of items and substitute your "Apple" strings for empty strings.
Use a std::vector of strings and whether a) initialize it with the array of items and then call std::erase_if (C++20) to remove the "Apple" strings, or b) initialize it without elements and then call std::copy_if together with std::back_inserter to append the non-"Apple" strings.
[Demo]
#include <algorithm> // copy_if, transform
#include <iostream> // cout
#include <string>
#include <vector> // erase_if
int main()
{
{
std::string items[10] = { "Apple", "Oranges", "Pears", "Apple", "bananas", "Apple", "Cucumbers", "Apple", "Lemons", "Apple" };
std::transform(std::begin(items), std::end(items), std::begin(items), [](auto& s) {
return (s == "Apple" ? "" : s);
});
for (const auto& s : items) { std::cout << s << ", "; }
std::cout << "\n";
}
{
const std::string items[10] = { "Apple", "Oranges", "Pears", "Apple", "bananas", "Apple", "Cucumbers", "Apple", "Lemons", "Apple" };
std::vector<std::string> v{std::cbegin(items), std::cend(items)};
std::erase_if(v, [](auto& s) { return s == "Apple"; });
for (const auto& s : v) { std::cout << s << ", "; }
std::cout << "\n";
}
{
const std::string items[10] = { "Apple", "Oranges", "Pears", "Apple", "bananas", "Apple", "Cucumbers", "Apple", "Lemons", "Apple" };
std::vector<std::string> v{};
std::copy_if(std::cbegin(items), std::cend(items), std::back_inserter(v), [](auto& s) {
return s != "Apple";
});
for (const auto& s : v) { std::cout << s << ", "; }
}
}
// Outputs:
//
// , Oranges, Pears, , bananas, , Cucumbers, , Lemons, ,
// Oranges, Pears, bananas, Cucumbers, Lemons,
// Oranges, Pears, bananas, Cucumbers, Lemons,
The simplest and most efficient way to remove a number of elements in an array is to create a new array:
#include <iostream>
using namespace std;
int main()
{
string items[10] = { "Apple", "Oranges", "Pears", "Apple", "bananas", "Apple", "Cucumbers", "Apple", "Lemons", "Apple" };
string new_items[10];
int new_size = 0;
// construct new array
for (int i = 0; i < sizeof(items)/sizeof(string); i++) {
if ("Apple" != items[i]) {
new_items[new_size++] = items[i];
}
}
// print new array
for (int i = 0; i < new_size; i++) {
cout << new_items[i] << " ";
}
cout << endl;
return 0;
}
I have the following vector of vector containning tree string elements, I want to know how to iterate over col element instead of normal loop over rows
vector<vector<string>> vec2Dstr =
{
{ "A2", "A4", "A6" },
{ "B2", "B4", "B6" },
{ "C2", "C4", "C6" },
{ "D2", "D4", "D6" },
{ "E2", "E4", "E6" }
};
the output expected is :
A2 B2 C2 D2 E2
A4 B4 C4 D4 E4
A6 B6 C6 D6 E6
code
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<vector<string>> vec2Dstr =
{
{ "A2", "A4", "A6" },
{ "B2", "B4", "B6" },
{ "C2", "C4", "C6" },
{ "D2", "D4", "D6" },
{ "E2", "E4", "E6" }
};
for(auto & j : vec2Dstr ) {
for(int i = 0 ; i < 3; i++ )
cout << " => " <<j.at(i) << endl;
}
return 0;
}
this following code might do the aim expected!
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<vector<string>> vec2Dstr =
{
{ "A2", "A4", "A6" },
{ "B2", "B4", "B6" },
{ "C2", "C4", "C6" },
{ "D2", "D4", "D6" },
{ "E2", "E4", "E6" }
};
size_t total_col = vec2Dstr[0].size();
for (int col=0; col<total_col; ++col)
{
for(auto& row : vec2Dstr)
{
cout << row[col] << " ";
}
cout << endl;
}
return 0;
}
Your for loops are wrong. How do you expect it to show 5 elements when you hardcode a limit of 3. Rewrite the for loop using this logic:
Iterate vec2Dstr from 0 to length.
Display vec2Dstr[i][j] where j increases after each loop we described above.
If you still are stuck after this let me know. Show me some code first.
i know my question can be stupid for somebody, but i googled all day and try make my own solution but i failed.. Please help..
I need print all uniqe string from an simple array of strings.
example:
input: "Hi" "my" "name" "Hi" "potato" "text" "name" "Hi"
output: "my" "potato" "text"
I make just function to print everything once ("Hi", "my", "name", "potato", "text"), but i need ignore everything what is 2x and more times in array.
My algorythm was:
1. sort by bubblesort
print only last string from sorted sequence by using basic for and if
.. if(array[i]!=array[i+1]) //make something...
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<std::string> v = {
"Hi", "my", "name", "Hi", "potato", "text", "name", "Hi",
};
sort(v.begin(), v.end());
for (auto a = v.begin(), b = a; a != v.end(); a = b) {
b = find_if(b, v.end(), [&](string s) {return *b != s;});
if (distance(a, b) == 1)
cout << *a << '\n';
}
}
update : i was misunderstanding the question now it works as output in the question
simply you can count the occurrences of every string and print only strings which occur ones..
time complexity : O(N^2)
here is the code
#include<iostream>
#include<set>
#include <string>
#include <vector>
using namespace std;
int main()
{
int n; // number of strings you want in your array
cin >> n;
string t; // get t and push back it in the vector
vector <string> words; //we use vector to store as we will push back them
for(size_t i = 1;i <= n;i++)
{
cin >> t;
words.push_back(t);
}
for(int i = 0;i < words.size();i++)
{
int cnt = 0;
for(int j = 0;j < words.size() && cnt < 2;j++)
{
if(words[i] == words[j])
cnt++;
}
if(cnt == 1) //its unique..print it
cout << words[i] <<endl;
}
}
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> words{"Hi", "my", "name", "Hi", "potato", "text", "name", "Hi"};
std::sort(words.begin(), words.end());
for (auto curr = words.begin(); curr != words.end(); ) {
// If working w/ few duplicate words:
auto next = std::find_if(
curr + 1, words.end(), [&](const auto& s) { return s != *curr; }
);
/* If working w/ many duplicate words:
auto next = std::upper_bound(curr + 1, words.end(), *curr); */
if (std::distance(curr, next) == 1) {
std::cout << *curr++ << '\n';
} else {
curr = next;
}
}
}
Demo
std::sort, std::upper_bound, std::find_if, std::distance
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> words{"Hi", "my", "name", "Hi", "potato", "text", "name", "Hi"};
std::vector<std::string> out;
std::sort(words.begin(), words.end());
std::unique_copy(words.begin(), words.end(), std::back_inserter(out));
std::set_difference(words.begin(), words.end(), out.begin(), out.end(), std::ostream_iterator<std::string>(std::cout, " "));
}
Note: Not tested as I am writing this on my phone and in bed.
#include<iostream>
#include<string>
using namespace std;
int main()
{
int i,j;
string ss;
cin>>ss;
for(i=0;i<ss.length();i++)
{
for(j=1;j<ss.length();j++)
{
if(ss[i]==ss[j]&&i!=j)
{
ss[i]=' ';
}
}
}
for(i=0;i<ss.length();i++)
{
if(ss[i]!=' ')cout<<ss[i]<<" "; /// for unique element print
}
return 0;
}
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
int a[n],i,j,k;
for(i=0;i<n;i++)
{
cin>>a[i];
}
for(i=0;i<n;i++)
{
for(j=1;j<n;j++)
{
if(a[i]==a[j]&&i!=j)
{
a[i]=0;
}
}
}
for(i=0;i<n;i++)
{
if(a[i]!=0)cout<<a[i]<<" ";
}
}
Consider the following vector
vector<vector<string>> a_words(80000,vector<string>(3));
which is a three dimension vector;
Now consider the following elements:
Joan Williams 30
Mike Williams 40
Joan Smith 30
William Anderson 20
Sara Jon 33
Basically I want to search by row, and I want to find Joan Williams, keep in mind that Joan is an element in the first column and Williams is an element is the second column
Should I use the "find" function? if yes how would it be written, else which function should I use?
Here are two demonstrative programs one for C++ 2003 and other for C++ 2011 that do the search
C++ 2003
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
#include <functional>
struct FindName : std::unary_function<bool,
const std::pair<std::string, std::string>>
{
FindName( const std::pair<std::string, std::string> &p ) : p( p ){}
bool operator ()( const std::vector<std::string> &v ) const
{
return v.size() > 1 &&
v[0] == p.first && v[1] == p.second;
}
protected:
const std::pair<std::string, std::string> p;
};
int main()
{
const size_t N = 5;
std::vector<std::vector<std::string>> v;
v.reserve( N );
const char * initial[N][3] =
{
{ "Joan", "Williams", "30" },
{ "Mike", "Williams", "40" },
{ "Joan", "Smith", "30" },
{ "William", "Anderson", "20" },
{ "Sara", "Jon", "33" }
};
for ( size_t i = 0; i < N; i++ )
{
v.push_back( std::vector<std::string>( initial[i], initial[i] + 3 ) );
}
std::pair<std::string, std::string> p( "Joan", "Williams" );
typedef std::vector<std::vector<std::string>>::iterator iterator;
iterator it = std::find_if( v.begin(), v.end(), FindName( p ) );
if ( it != v.end() )
{
for ( std::vector<std::string>::size_type i = 0; i < it->size(); ++i )
{
std::cout << ( *it )[i] << ' ';
}
}
std::cout << std::endl;
}
C++ 2011
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <utility>
int main()
{
std::vector<std::vector<std::string>> v =
{
{ "Joan", "Williams", "30" },
{ "Mike", "Williams", "40" },
{ "Joan", "Smith", "30" },
{ "William", "Anderson", "20" },
{ "Sara", "Jon", "33" }
};
std::pair<std::string, std::string> p( "Joan", "Williams" );
auto it = std::find_if( v.begin(), v.end(),
[&]( const std::vector<std::string> &row )
{
return row.size() > 1 &&
row[0] == p.first && row[1] == p.second;
} );
if ( it != v.end() )
{
for ( const auto &s : *it ) std::cout << s << ' ';
}
std::cout << std::endl;
}
The both programs' putput is
Joan Williams 30
I strongly advise you to use a data structure with an overloaded equality operator instead of vector<string> (especially since it seems like the third element should be saved in an integer, not a string).
Anyway, this is one possibility:
auto iter = std::find_if( std::begin(a_words), std::end(a_words),
[] (std::vector<std::string> const& vec)
{ return vec[0] == "Joan" && vec[1] == "Williams";};
If the list is lexicographically sorted by the first or second column, a binary search can be used instead.
As of C++11, a range based for loop would be a simple and readable solution:
for(auto r: a_words)
if(r[0] == "Joan" && r[1] == "Williams")
cout << r[0] << " " << r[1] << " " << r[2] << endl;
Essentially the answer of #Columbo is nice, eliminating C++ 11 features (besides initialization):
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
int main() {
// Requires C++11
std::vector<std::vector<std::string>> words = {
{ "Joan", "Williams", "30" },
{ "Mike", "Williams", "40" },
{ "Joan", "Smith", "30" },
{ "William", "Anderson", "20" },
{ "Sara", "Jon", "33" },
};
// Below does not require C++11
struct EqualName
{
const char* first;
const char* second;
EqualName(const char* first, const char* second)
: first(first), second(second)
{}
bool operator () (const std::vector<std::string>& element) {
return element[0] == first && element[1] == second;
}
};
std::vector<std::vector<std::string>>::const_iterator
pos = std::find_if(words.begin(), words.end(), EqualName("Joan", "Smith"));
if(pos != words.end())
std::cout << (*pos)[0] << ' ' << (*pos)[1] << ' ' << (*pos)[2] << '\n';
}
if I uncomment these
//BaseList baselist;
//MemberList memberlist;
outside the loop and comment out the ones inside the loop it crashes. I need to be able to have the baselist (and memberlist) outside any loop. How is this achieved?
Edit
The actual problem I am trying to solve in it's simplest form is this.
I want to have a std::vector of MyClass, call it AllThingsBunchedTogether.
I also want to have a std::vector of BaseList, call it AllThingsSpreadOut.
So
AllThingsBunchedTogether might contain (just the anInt1 part for the sake of compactness): 1,2,1,10,2,3,4,4,5,9,10,10.
AllThingsSpreadOut might contain (zero not used for now) at [1] 1,1 at [2] 2,2 at [3] 3 at [4] 4,4 at [5] 5 at [9] 9 at [10] 10,10,10.
Note that the numbers themselves aren't be stored in the BaseList, but e.g., the MyClass(1, "John").
At [1] it could be "Mike", "John", at [2] it could be "Mike", "Dagobart" at [3]
"John" ... at [10] "John" "Mike" "Dagobart" etc so that there no duplicates in
any of the BaseList at AllThingsSpreadOut[i] since each MyClass in each
BaseList hashes to a different value (anInt1 + Name).
In essence, anInt1 tells where the MyClass lives in AllThingsSpreadOut, but anInt1 + name guarantees uniqueness within each BaseList.
So the idea is that AllThingsSpreadOut is a vector of BaseList where at each BaseList at vector location is a list of similar things.
Then, when I remove things from AllThingsBunchedTogether (not by a clear, but by a search to remove some items like in the code below IsMarkedToDelete), they will automatically disappear from the corresponding AllThingsSpreadOut.
AllThingsSpreadOut acts as a sort for AllThingsBunchedTogether, with intrusive semantics. AllThingsBunchedTogether allows superfast access through [].
End Edit
#include <vector>
#include <iostream>
#include <boost/intrusive/list.hpp>
using namespace boost::intrusive;
class MyClass : public list_base_hook<link_mode<auto_unlink>> // This is a derivation hook
{
public:
std::string name;
bool bIsMarkedToDelete;
int anInt1;
public:
list_member_hook<link_mode<auto_unlink>> member_hook_; // This is a member hook
MyClass(std::string n, int i) : name(n), anInt1(i), bIsMarkedToDelete(false) {}
};
bool IsMarkedToDelete(const MyClass &o)
{
return o.bIsMarkedToDelete;
}
//Define a list that will store MyClass using the public base hook
typedef list<MyClass, constant_time_size<false>> BaseList;
// Define a list that will store MyClass using the public member hook
typedef list<MyClass,
member_hook<MyClass, list_member_hook<link_mode<auto_unlink>>, &MyClass::member_hook_>,
constant_time_size<false> > MemberList;
int main()
{
bool done = false;
std::vector<MyClass> values;
std::string names[] = {"John", "Mike", "Dagobart"};
//BaseList baselist;
//MemberList memberlist;
int i = 0;
while(!done)
{
// Create several MyClass objects, each one with a different value
for (int j = 0; j < 11; ++j)
values.emplace_back(names[j % 3], j);
BaseList baselist;
MemberList memberlist;
// Now insert them in t-he reverse order in the base hook list
for (auto& e : values)
{
baselist.push_front(e);
memberlist.push_back(e);
}
// Now test lists
auto rbit(baselist.rbegin());
auto mit(memberlist.begin());
auto it(values.begin()), itend(values.end());
// Test the objects inserted in the base hook list
for (; it != itend; ++it, ++rbit)
{
if (&*rbit != &*it)
return 1;
}
// Test the objects inserted in the member hook list
for (it = values.begin(); it != itend; ++it, ++mit)
{
if (&*mit != &*it)
return 1;
}
# if 0
for(auto& e : values)
std::cout << e.anInt1 << "\n";
for(auto& e : baselist)
std::cout << e.anInt1 << "\n";
for(auto& e : memberlist)
std::cout << e.anInt1 << "\n";
#endif // 0
if(2 == i)
{
for(auto& e: values)
std::cout << e.name << "\n";
for(auto& e: values)
{
if("Mike" == e.name)
e.bIsMarkedToDelete = true;
}
values.erase(
std::remove_if(values.begin(), values.end(), IsMarkedToDelete), values.end());
}
if(i++ > 3)
{
values.clear();
done = true;
}
std::cout << "\n";
std::cout << values.size() << "\n";
std::cout << baselist.size() << "\n";
std::cout << memberlist.size() << "\n";
}
}
I've seen it late, but anyways, here goes:
What you describe matches exactly the implementation of an intrusive hash table of MyClass elements, where
anInt1 is the hash (the bucket identifier) for an element
the bucket lists are implemented as linked lists
equality is defined as equality of (anInt1, Name)
So really, your program could just be:
Live On Coliru
std::unordered_set<MyClass> values {
{ "John", 0 }, { "Mike", 1 }, { "Dagobart", 2 },
{ "John", 3 }, { "Mike", 4 }, { "Dagobart", 5 },
{ "John", 6 }, { "Mike", 7 }, { "Dagobart", 8 },
{ "John", 9 }, { "Mike", 10 },
};
for(int i = 0; i<=3; ++i) {
if(2 == i) {
for(auto& e: values) std::cout << e.name << " "; std::cout << "\n";
for(auto& e: values) e.bIsMarkedToDelete |= ("Mike" == e.name);
for(auto it=begin(values); it!=end(values);) {
if (it->bIsMarkedToDelete) it = values.erase(it);
else ++it;
}
}
std::cout << "i=" << i << ", values.size(): " << values.size() << "\n";
}
values.clear();
std::cout << "Done\n";
if you really wanted contiguous storage, I can only assume you wanted this for performance
you do not want to use pointers instead of objects, since that simply negates the memory layout ("AllThingsBunchedTogether") benefits and you'd be better of with the unordered_set or unodered_map as above
you do not want to use auto_unlink mode, since it cripples performance (by doing uncontrolled deletion triggers, by inhibiting constant-time size() and by creating thread safety issues)
instead, you should employ the above stratagy, but with boost::intrusive::unordered_set instead see http://www.boost.org/doc/libs/1_57_0/doc/html/intrusive/unordered_set_unordered_multiset.html
Here, again, is a proof-of-concept:
Live On Coliru
#include <vector>
#include <iostream>
#include <boost/intrusive/unordered_set.hpp>
#include <vector>
//#include <functional>
//#include <algorithm>
namespace bic = boost::intrusive;
struct MyClass : bic::unordered_set_base_hook<bic::link_mode<bic::auto_unlink>>
{
std::string name;
int anInt1;
mutable bool bIsMarkedToDelete;
MyClass(std::string name, int i) : name(name), anInt1(i), bIsMarkedToDelete(false) {}
bool operator==(MyClass const& o) const { return anInt1 == o.anInt1 && name == o.name; }
struct hasher { size_t operator()(MyClass const& o) const { return o.anInt1; } };
};
typedef bic::unordered_set<MyClass, bic::hash<MyClass::hasher>, bic::constant_time_size<false> > HashTable;
int main() {
std::vector<MyClass> values {
MyClass { "John", 0 }, MyClass { "Mike", 1 }, MyClass { "Dagobart", 2 },
MyClass { "John", 3 }, MyClass { "Mike", 4 }, MyClass { "Dagobart", 5 },
MyClass { "John", 6 }, MyClass { "Mike", 7 }, MyClass { "Dagobart", 8 },
MyClass { "John", 9 }, MyClass { "Mike", 10 },
};
HashTable::bucket_type buckets[100];
HashTable hashtable(values.begin(), values.end(), HashTable::bucket_traits(buckets, 100));
for(int i = 0; i<=3; ++i) {
if(2 == i) {
for(auto& e: values) std::cout << e.name << " "; std::cout << "\n";
for(auto& e: values) e.bIsMarkedToDelete |= ("Mike" == e.name);
values.erase(std::remove_if(begin(values), end(values), std::mem_fn(&MyClass::bIsMarkedToDelete)));
}
std::cout << "i=" << i << ", values.size(): " << values.size() << "\n";
std::cout << "i=" << i << ", hashtable.size(): " << hashtable.size() << "\n";
}
values.clear();
std::cout << "Done\n";
}
Here's the error message, which you omitted:
Assertion `node_algorithms::inited(to_insert)' failed.
From this we can understand that an element is being inserted twice. This isn't valid with intrusive containers in general.
When you have your lists inside the loop, they are destroyed and recreated each time. But when they are outside, you never clear them, and you also never clear values, so this sequence occurs:
Add 11 elements to values.
Add all values to the lists.
Add 11 elements to values; it still has the previous 11 so now 22 elements.
Add all values to the lists. Crash on the first one, because it is already in a list.
One solution is to add values.clear() at the top of the while(!done) loop.