How to access member function with iterator in C++ std::set - c++

I am trying to iterate over a C++ std::set and access a member function. But my following code is not working.
#include <iostream>
#include <set>
using namespace std;
class A;
std::set<A> m_vector;
std::set<A>::iterator iter = m_vector.begin();
class A
{
public:
int age;
A()
{
cout << "enter age" << endl;
cin >> age;
}
int getAge()
{
return age;
}
private:
};
void addNewVoter()
{
m_vector.insert(A());
}
int main()
{
addNewVoter();
addNewVoter();
addNewVoter();
for (size_t i = 0; i < m_vector.size(); i++)
{
cout << (*iter)->getAge(); /* Here is the mistake. I can't invoke this.*/
iter++;
}
}
My question is, how can I invoke member function with iterator?

Initialize iter when you are ready to traverse. Also, access member(s) by either iter->getAge() or (*iter).getAge().
#include <iostream>
#include <set>
using namespace std;
class A;
std::set<A> m_vector;
std::set<A>::iterator iter;
/* other codes */
int main()
{
addNewVoter();
addNewVoter();
addNewVoter();
iter = m_vector.begin();
while( iter != m_vector.end() )
{
cout << iter->getAge();
iter++;
}
}

Three changes are required in your code snippet. std::set internally implementing a red black binary tree and it required an operator overloading for comparison.
So you class should have an additional function:
bool operator <(const A& other) const
{
return age < other.age;
}
Then while you call getAge() function system passing a const object of A, so your getAge function should be a const function.
int getAge() const
^^^^^
{
return age;
}
Third one, you should invoke the getAge function either using one of these syntax. (*iter).getAge() or iter->getAge(); dont mix it up.
Demo

Edit first:
Let's say you at least know how to invoke that member function through pointer. The answer is iter->getAge() or (*iter).getAge() - in the comments.
Then you're doing this:
std::set<A> my_set;
auto it = my_set.begin();
// some inserts
std::cout << it->getAge() << std::endl;
which is undefined behaviour. You don't dereference begin iterator acquired originally from an empty container.
Just use this, instead:
for (auto it = my_set.begin(); it != my_set.end(); ++it)
std::cout << it->getAge() << std::endl;
or the range-based for...
As you can see, I refused to use your naming conventions because they're bad and misleading.

Related

STL and std custom compare arguments working in c++

I asked this question on stackoverflow STL passing object
I got to know that we pass objects which in tern call the compare operator in them and compare our values and gives us a result. All good.
Now in this piece of code:
class Compare {
public:
bool operator ()(const int &a, const int &b) { return a < b;}
};
void solve(){
set<int, Compare> s;
vector<int> arr = {1,4,6,3,7,2,8,588,5};
for(int e : arr) s.insert(e);
for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;
}
I get error if I do Compare() which is what I thought must be done(passing an object). Why so? How can we pass Compare?
Also in this piece of code I cannot pass a function like:
bool comp(const int &a, const int &b) { return a < b; }
void solve(){
set<int, comp> s;
vector<int> arr = {1,4,6,3,7,2,8,588,5};
for(int e : arr) s.insert(e);
for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;
}
Which is what I expect. Not passing functions.
Now in this piece of code:
bool comp(const int &a, const int &b) { return a < b; }
void solve(){
vector<int> arr = {1,4,6,3,7,2,8,588,5};
nth_element(arr.begin(), arr.begin()+3, arr.end(), comp);
for(int e : arr) cout << e << " "; cout << endl;
}
I am able to pass a function and there is no error. I mean what is going on? From my perspective, we should just pass objects and they will call their () overloaded operator and compare values but sometimes we are passing functions, sometimes passing objects giving error and sometimes we just pass class name. What is exactly going on in c++
Second template parameter of std::set is a type.
You might use function pointer:
bool comp(const int &a, const int &b) { return a < b; }
std::set<int, bool (*)(const int &, const int &)> s(comp);
In the first code snippet the compiler issues an error because you was using an object Compare() as the type template argument instead of the type Compare.
If you will write for example
#include <iostream>
#include <vector>
#include <set>
using namespace std;
class Compare {
public:
bool operator ()(const int &a, const int &b) const { return a < b;}
};
int main()
{
set<int, Compare> s( ( Compare() ) );
vector<int> arr = {1,4,6,3,7,2,8,588,5};
for(int e : arr) s.insert(e);
for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl;
return 0;
}
then the program compiles successfully and its output is
588 8 7 6 5 4 3 2 1
That is as the second type template argument of the class template std::set you need to use the type specifier Compare. But if you want to specify the argument of the constructor you need to use an object of the type of the template argument.
The same problem exists for the second code snippet. You need to write like it is shown in this demonstrative program.
#include <iostream>
#include <vector>
#include <set>
using namespace std;
bool comp(const int &a, const int &b) { return a < b; }
int main()
{
set<int, bool ( * )( const int &, const int & )> s( comp );
vector<int> arr = {1,4,6,3,7,2,8,588,5};
for(int e : arr) s.insert(e);
for(auto it = s.rbegin();it!=s.rend();it++) cout << *it << ' '; cout << endl; return 0;
}
The program output is the same as shown above.
Pay attention to that the class template std::set is declared like
template <class Key, class Compare = less<Key>,
class Allocator = allocator<Key> >
class set;
So if you specified explicitly the second template argument (instead of using by default the function object std::less<Key>) then you need to provide a corresponding object of this type in a constructor of the class template std::set.
operator() must be const. That's the API.
class Compare {
public:
bool operator ()(const int &a, const int &b) const { return a < b; }
}

Creating vector of custom complex number class using std::vector

I am new to coding and currently learning C++ to get started, so please excuse my narrow knowledge and very possible mistakes in code and general C++ lingo.
I have written a class of complex numbers called ComplexNumber with member variables being double re and double im with the obvious meanings. This class has a constructor, an empty constructor, a copy constructor, a destructor etc.
Using this I am now meaning to write a class ComplexVector of vectors with entries from ComplexNumber. For now I have defined the member variables to be a vector std::vector<ComplexNumber> vec and a size int size. Additionally I would like to define a member function void print(), again with the obvious meaning. The code I have so far is:
#include <iostream>
#include <vector>
#include "complex.h"
class ComplexVector {
private:
std::vector<ComplexNumber> vec;
int size;
public:
ComplexVector(std::vector<ComplexNumber> vector, int n){ //constructor
size = n;
vec = vector;
};
ComplexVector(){ //empty constructor
size = 0;
vec = {};
};
void print() const;
~ComplexVector(); //destructor
ComplexVector(const ComplexVector& v); //copy constructor
ComplexVector addition(ComplexVector w); //
ComplexVector subtraction(ComplexVector w); // i am not worrying about these for now
ComplexVector scale(ComplexNumber z); //
};
with defnitions
void ComplexVector::print() const {
for(std::vector<ComplexNumber>::iterator it = vec.begin(); it != vec.end(); it++){
std::cout << *it << " ";
};
std::cout << std::endl;
};
ComplexVector::~ComplexVector(){
std::cout << "calling destructor" << std::endl;
};
ComplexVector::ComplexVector(const ComplexVector& v){
size = v.size;
vec = v.vec;
};
Here is where I am getting a compiling error: in the definition of the print my compiler tells me
error: no viable conversion from '__wrap_iter<std::__1::vector<ComplexNumber, std::__1::allocator<ComplexNumber>
>::const_pointer>' to '__wrap_iter<std::__1::vector<ComplexNumber, std::__1::allocator<ComplexNumber> >::pointer>'
for(std::vector<ComplexNumber>::iterator it = vec.begin(); it != vec.end(); it++){
which I am not quite sure how to deal with. I have read something about certain member functions having to be defined for ComplexNumber to be used in an std::vector. I also played around with defining iterators inside of ComplexVector but this did not solve the problem. From my (to be fair very narrow) perspective there should be an iterator for vec and also corresponding begin()and end() functions. I have checked if I am passing arguments of the right type a thousand times, but I must be overlooking something.
One more thing to note is that I am very much aware that this is probably an extremely inefficient way to define a class like this. I have looked at multiple examples which used pointers to an array as the main member variable. I am surely going to implement something of that type next, but for now I want to understand where the mistake in my current code is. So thanks in advance for any answers.
One side question: I don't think I have understood the concept of a destructor very well (to be fair I haven't spent much time reading about it yet), but if anyone has a quick intuition about them, which he/she wants to share, that would be highly appreciated.
Also if you have any comments on style and/or other improvements of my code, I would appreciate it if you could share them.
Thank you!
Edit: Here is the ComplexNumberclass
class ComplexNumber {
private:
double re;
double im;
public:
ComplexNumber(double x, double y){
re = x;
im = y;
};
ComplexNumber(){
re = im = 0;
};
ComplexNumber(const ComplexNumber& z);
void print() const;
};
and definitions
void ComplexNumber::print() const {
if(im > 0){
std::cout << re << "+" << im << "i" << std::endl;
} else if(im < 0){
std::cout << re << im << "i" << std::endl;
} else {
std::cout << re << std::endl;
};
};
ComplexNumber::ComplexNumber(const ComplexNumber& z){
re = z.re;
im = z.im;
};
And a main method:
int main() {
std::vector<ComplexNumber> v1 = {ComplexNumber(1,2), ComplexNumber(4,2)};
int n = 2;
ComplexVector w1(v1,n);
w1.print();
return 0;
}
Your problem is here:
void ComplexVector::print() const {
for(std::vector<ComplexNumber>::iterator it = vec.begin(); it != vec.end(); it++){
std::cout << *it << " ";
};
std::cout << std::endl;
};
Notice that the function is marked const. This means any members of the class are treated as const qualified inside the method (i.e. you are not allowed to modify them). Thus when you call vec.begin() you are calling the const version of begin() on vector. This returns const_iterator not an iterator.
std::vector<ComplexNumber>::iterator it = vec.begin()
^^^^^^^^ should be const_iterator
A better way to solve this is to use auto and let the compiler work out the correct type:
auto it = vec.begin()
So the function becomes:
void ComplexVector::print() const {
for(std::vector<ComplexNumber>::const_iterator it = vec.begin(); it != vec.end(); it++){
std::cout << *it << " ";
};
std::cout << std::endl;
}
or with auto:
void ComplexVector::print() const {
for(auto it = vec.begin(); it != vec.end(); it++){
std::cout << *it << " ";
};
std::cout << std::endl;
}
or you can improve this by using the new version of for()
void ComplexVector::print() const {
for(auto const& item: vec){
std::cout << item << " ";
};
std::cout << "\n";
}
Note: When your code works. You can ask for a review on style at https://codereview.stackexchange.com
Follow up based on comments:
Try:
class ComplexNumber
{
......
friend std::ostream& operator<<(std::ostream& str, ComplexNumber const& data) {
data.print(); // You want to change this
// so you can pass the stream to print.
return str;
}
}

C++ Sorting vector of structs with const variables alphabetically

Hello I wanted to know if it was possible to do a thing like this ? // THANKS ! :)
struct PET
{
const char* pet;
const int age;
};
bool Sort(const PET& first, const PET& second)
{
return first.pet < second.pet;
}
void Foo(const std::vector<PET> pets)
{
std::sort(pets.begin(), pets.end(), Sort); /* Does not work */
std::cout << pets[0].pet;
std::cout << pets[0].age;
}
I completely agree with # Ulrich Eckhardt.
You cannot sort the vector as because the elements of your vector are not assignable.
I think, you might have gone confused with usage of const.
There is no need to make the structure variables const. The parameter of the custom sort function are generally kept as const because they should not be modifiable. This is a pattern which ensures safe coding practice.
Also, if you are using C++, I would suggest to use std::string instead of char*, as std::string is a cleaner, safer way to go because it removes the burden of memory management from the programmer.
Have a look at the working implementation, without use of const:
#include <string.h>
#include<iostream>
#include<vector>
#include<algorithm>
struct PET
{
std::string name;
int age;
};
bool compare(const struct PET& a, const struct PET& b){
return (a.name.compare(b.name) <= 0) ? true : false;
}
int main(){
std::vector<struct PET> vec(3);
vec[0].name = "dog";
vec[0].age = 3;
vec[1].name = "cat";
vec[1].age = 1;
vec[2].name = "bird";
vec[2].age = 2;
sort(vec.begin(), vec.end(), compare);
for(int i=0;i<3;i++){
std::cout<<vec[i].name<<" "<<vec[i].age<<std::endl;
}
return 0;
}
As #Deepak Tatyaji Ahire and #Ulrich Eckhardt said, you can't do what you wrote in your code.
const int can't be a variable. It is a constant for definition :)
The vector you wrote in your code can't be built that way.
I did not understand what you wanted to do with the "sort" function, I wrote the following to code, maybe it could help:
#include<iostream>
#include<vector>
struct PET
{
const char* pet;
int age;
PET(const char* c, int a) : pet(c) , age(a) {}
};
void Foo(PET &p, std::vector<PET> &v)
{
v.push_back(p);
/*do something here if needed*/
}
int main()
{
std::vector<PET> vect;
PET cat("Cat", 5);
PET dog("Dog", 10);
PET bird("Bird", 2);
Foo(cat, vect);
Foo(dog, vect);
Foo(bird, vect);
/*this is not elegant, you could define a function that give a list of
({Animal, age},...) to vector and then pushes back all these elements to the vector*/
for(int i=0; i<3; i++) std::cout<< vect[i].pet << ' ' << vect[i].age << std::endl; //std::cout << vect; if you are using an operator << overload
/*to overload the << operator in order to able to print the vector of struct PET:
std::ostream & operator << (std::ostream &os, std::vector<PET> &p)
{
os << "<";
for (int i = 0; i < p.size(); i++) {
os << p[i].pet;
os << ", ";
os << p[i].age;
if (i != p.size() - 1)
os << " - ";
}
os << ">\n";
return os;
}
*/
return 1;
}
AFAIK, there's no way to directly compare structures without defining their comparator.
Though in C++20, it introduces three-way comparison and you might be allowed to declare the Default comparisons by a single line. Very convenient. Unfortunately, there haven been no compiler implementing this feature.
For now, you have to manually define the comparator
inline bool cmp(const PET &lhs, const PET &rhs)
{
return std::strcmp(lhs.pet, rhs.pet)<0;
}
and pass it to std::sort

Invalid initialization of non-const reference of type 'AssortedSorter&' from an rvalue of type BubbleSort*"

I have a program that I'm writing that has an abstract base class "AssortedSorted", a derived class "BubbleSort", and a a class to test sorts "AssortedSorterTest".
The Idea is to create a bubbleSort instance, pass that instance to an instance of assortedSorterTest along with an int for the quantity of random numbers to create and sort, and return true from assortedsorter.testSort() method if the vector is sorted and contains the same number of elements as the vector it was given.
If you read the code, there are things that need to be changed to accomplish this, but I'm not concerned with correcting those yet unless they are relevant to the issue I am currently having regarding invalid initialization on line 16 in main.cpp. The error that I am getting is this
"invalid initialization of non-const reference of type 'AssortedSorter&' from an rvalue of type BubbleSort*"
Initially I thought that adding #include "BubbleSort.h" to the AssortedSorterTest class may correct the problem, it did not. I've also tried changing some of the references to pointers, this created new problems for me, so I switched back to references. I haven't had any luck in figuring this out, so any healp would be appreciated.
#pragma once
#include <vector>
#include <string>
class AssortedSorter
{
public:
virtual std::vector<int> sort(const std::vector<int> &itemsToSort) = 0;
virtual std::string getName() const = 0;
virtual ~AssortedSorter() {};
};
#include <sstream>
class BubbleSort : public AssortedSorter
{
private:
long loopCount{0};
long swapCount{0};
public:
BubbleSort();
~BubbleSort() override;
std::vector<int> sort(const std::vector<int> &itemsToSort) override;
std::string getName() const override;
friend std::ostream &operator<<(std::ostream &out, const BubbleSort &rhs);
};
#include "BubbleSort.h"
BubbleSort::BubbleSort()
{
}
BubbleSort::~BubbleSort()
{
}
std::vector<int> BubbleSort::sort(const std::vector<int> &itemsToSort)
{
std::vector<int> itemsSorted = itemsToSort;
bool swap{false};
int temporary_num{};
do
{
swap = false;
for (int index = 0; index < itemsSorted.size()-1; index++)
{
loopCount++;
if (itemsSorted[index] > itemsSorted[index + 1])
{
swapCount++;
temporary_num = itemsSorted[index];
itemsSorted[index] = itemsSorted[index + 1];
itemsSorted[index + 1] = temporary_num;
swap = true;
}
}
} while (swap);
return itemsSorted;
}
std::string BubbleSort::getName() const
{return "BubbleSort";}
//Overloaded insertion operator
std::ostream &operator<<(std::ostream &os, const BubbleSort &rhs)
{
os << rhs.getName() << ": " << std::to_string(rhs.loopCount) << " " << std::to_string(rhs.swapCount);
return os;
}
#pragma once
#include "AssortedSorter.h"
#include <vector>
class AssortedSorterTest
{
public:
AssortedSorterTest();
~AssortedSorterTest();
bool testSort(AssortedSorter &assortedSorter, int size);
};
#include "AssortedSorterTest.h"
AssortedSorterTest::AssortedSorterTest()
{
}
AssortedSorterTest::~AssortedSorterTest()
{
}
bool testSort(AssortedSorter &assortedSorter, int size)
{
std::vector<int> randomNumbers;
for(int index{0}; index < size; index++)
{
randomNumbers.push_back(rand());
}
std::vector<int> sortedVector = assortedSorter.sort(randomNumbers);
if(sortedVector == randomNumbers)
{
return true;
}
else
{
return false;
}
}
#include <iostream>
#include <vector>
#include <ctime>
#include <cstdlib>
#include "AssortedSorterTest.h"
#include "BubbleSort.h"
std::vector<int> assign_vector_values(int size);
int main()
{
std::vector<int> vec = assign_vector_values(100);
AssortedSorter &bubbleSort = new BubbleSort; //problem is here
AssortedSorterTest sortTester;
if(sortTester.testSort(bubbleSort, 100))
{
std::cout << "Vector has been sorted" << std::endl;
}
else
{
std::cout << "Vector has not been sorted properly" << std::endl;
}
delete bubbleSort;
return 0;
}
std::vector<int> assign_vector_values(int size)
{
std::vector<int> temp_vector;
for(int index{0}; index < size; index++)
{
temp_vector.push_back(rand());
}
return temp_vector;
}
The error message is telling you exactly what the issue is.
new BubbleSort results in a pointer to a BubbleSort.
You are trying to bind a reference to the base class of BubbleSort to it. That cannot work.
Either you need to dereference the pointer or you need to initialize a pointer, not a reference, with it.
In any case you should not use naked new/delete in modern C++. Use std::unique_ptr<AssortedSorter> and std::make_unique<BubbleSort>() instead:
std::unique_ptr<AssortedSorter> bubbleSort = std::make_unique<BubbleSort>();
This requires #include<memory>.
Or, given how exactly the code in main looks right now, there is no need for a dynamic allocation at all. Simply
BubbleSort bubbleSort;
will do as well.

C++ is it possible to have a container of specialized templates objects with different types parameters?

I have a templated class Parameter which can (or must) be specialized.
I want to put all my parameters in a container.
How to do this if my parameters are instanciated with different types?
In the class Container, I would like to have a vector<Parameter*> from different types (int, double, ...) or something equivalent which seems to not possible.
If the Parameter class is derived from a base class, then The Container can declare the vect as vector<Base*>. But in this case, we can do nothing specific in Container::foo.
Below is my source example. One of my parameters is a QString which is not compatible with ostream.
Thanks for your comments.
#include <QString>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
#define P(a) cout << #a << ":" << a << endl
/*
class Base {
};
*/
template<typename T> class Parameter /*: public Base */ {
private:
T val;
public:
void setVal(const T &val) {
this->val = val;
}
const T &getVal() {
return val;
}
string getFoo() {
stringstream s;
s << val;
return s.str();
}
};
template<>
string Parameter<QString>::getFoo() {
stringstream s;
s << val.toStdString();
return s.str();
}
class Container {
public:
void push_back(Parameter *base) {
vect.push_back(base);
}
void foo() {
/* do something with the parameters */
}
private:
vector<Parameter*> vect;
};
int main() {
Parameter<int> pi;
Parameter<QString> ps;
pi.setVal(10);
ps.setVal("QString");
P(pi.getVal());
P(ps.getVal().toStdString());
P(pi.getFoo());
P(ps.getFoo());
Container container;
container.push_back(&pi);
container.push_back(&ps);
}
Many thanks to you comments. I will follow your advice and use boost::any.
Here is the updated version :
#include <boost/any.hpp>
#include <QString>
#include <vector>
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
#define P(a) cout << #a << ":" << a << endl
template<typename T> class Parameter {
private:
T val;
public:
void setVal(const T &val) {
this->val = val;
}
const T &getVal() {
return val;
}
string getFoo() {
stringstream s;
s << val;
return s.str();
}
};
template<>
string Parameter<QString>::getFoo() {
stringstream s;
s << val.toStdString();
return s.str();
}
class Container {
public:
void push_back(boost::any base) {
vect.push_back(base);
}
void foo() {
cout << "do something with the parameters\n";
for (vector<boost::any>::iterator i = vect.begin(); i != vect.end(); ++i) {
boost::any a = (*i);
if (a.type() == typeid(Parameter<int>*)) {
Parameter<int> *ai = boost::any_cast<Parameter<int> *>(a);
cout << ai->getFoo() << endl;
} else if (a.type() == typeid(Parameter<QString>*)) {
Parameter<QString> *aq = boost::any_cast<Parameter<QString> *>(a);
cout << aq->getFoo() << endl;
} else {
cout << "unknown type:" << a.type().name() << endl;
}
}
}
private:
vector<boost::any> vect;
};
int main() {
Parameter<int> pi;
Parameter<QString> ps;
pi.setVal(10);
ps.setVal("QString");
P(pi.getVal());
P(ps.getVal().toStdString());
P(pi.getFoo());
P(ps.getFoo());
Container container;
container.push_back(&pi);
container.push_back(&ps);
container.foo();
}
The correct solution is to write good enough interface for the Base class so that you can do everything you need to do:
class Base {
public:
virtual void *GetVal() const=0;
virtual void SetVal(void *ptr)=0;
virtual std::string Type() const=0;
virtual std::string GetAsString() const=0;
};
While this might not be what you want, it still allows passing values from one parameter to the next. Once you want the actual value, you do need to know the type on compile-time. Switch-case for the type might help with making it runtime.
You could use Boost.Any which can hold any type of data. You would then use boost::any_cast<> to convert the object back to the correct type.
Other than that, you'll have to go for the base class approach, but as you mentioned, it could be hard to then make Container::foo do anything useful.
One way you could solve this problem is to have all your foo functions take a string as a parameter, then each specific implementation of the function would parse that string and convert it to the correct type.
Edit: Boost.Any example:
#include <iostream>
#include <boost/any.hpp>
int main()
{
boost::any param = 89;
// This will fail because `param` is currently holding an int
// not a char
char ch = boost::any_cast<char>(param);
// This works
int i = boost::any_cast<int>(param);
// You can always change the value and type of what
// `param` is holding
param = "example";
}
Every thing inside a container has to be the same type. I have done something similar to your approach where I made a base class that had some useful generic interface and the derived class was templated. The only other way to approach a solution would involve defining a base class function to return a value to indicate the type and then downcasting the base.