C++ Using find on a list of objects - c++

I can not seem to get std::find to work with a std::list of objects. I get the error "no match for ‘operator==’ (operand types are ‘Rabbit’ and ‘const int’) ". Fairly sure I need to utilize an object iterator and a lambda function, I'm just completely lost on how to do so and I just need some guidance. What I am ultimately trying to do is to iterator to a specific location in the list and pull the color string from that object in the list.
Edited to clarify the question and simplify the code
#include <iostream>
#include <list>
#include <algorithm>
#include "Rabbit.h"
#include "Population.h"
using namespace std;
int main()
{
Population Tracker;
Tracker.parentSeed(); //generate first random 8, and populate the list
Tracker.Breed(); //generate children, not working nothing happening as getMother does not work
return 0;
}
class Rabbit
{
protected:
const std::vector<std::string> namesList
{"Maxwell", "David", "Laura", "Sarah" , "Benjamin", "Carl",
"Rick", "Maggie", "Glenn", "Daryl", "Michonne", "Roseita",
"Leslie", "Randy", "Ethan", "Survan", "Leah", "Tisha", "Marcus"};
const std::vector<std::string> colorList
{"Red", "Green", "Blue",
"Grey", "Tan", "Brown",
"Calico", "White"};
public:
Rabbit(); //blank for the initial population
Rabbit(int); // pass an int for color inherited from mother
~Rabbit();
void getRabbit();
void randomNumGen(int);
std::string name;
std::string color;
};
Rabbit::Rabbit()
{
std::random_device random; //random seed obj
std::default_random_engine e1(random()); //get a random number seed
std::uniform_int_distribution<int> name_dist(0, 17); // vector position of name
std::uniform_int_distribution<int> color_dist(0, 7); // vector position of col
color = colorList[color_dist(e1)];
name = nameList[name_dist(e1)];
}
class Population
{
friend class Rabbbit;
protected:
public:
Population();
void popStats(int, bool);
void popList(Rabbit);
int getMother();
void parentSeed(); // generate initial population. All stats are random.
std::list<Rabbit> femaleList;
std::list<Rabbit> maleList;
std::list<Rabbit>::iterator male_it;
std::list<Rabbit>::iterator female_it;
};
int Population::getMother()
{
female_it++
//do something to iterate list and get data from the object at that position. Not to be sequential, just used that as example.
}
void Population::Breed()
{
/*
generate a new rabbit with color inhereited from the mother
father does not matter as all other stats are random
*/
if(maleList.size() > 2 && femaleList.size() > 2)
{
getMother(); // does nothing right now
std::cout << "Breed Success!" << std::endl;
Rabbit newRabbit;
popList(newRabbit);
}
}
void Population::parentSeed()
{
/*
Generate the initial seed count
*/
for (int i = 0; i < 8; i++)
{
Rabbit newRabbit;
popList(newRabbit);
}
}

The compiler is not aware of how it should distinguish between your Rabbits and raw integers. Thus, it's telling that you need to show it how to distinguish between them by defining an overloaded equality operator for your Rabbit objects and integers. You should add the following overloaded operators to your code:
bool
operator==(int const &i, Rabbit const &r) {
...
return ?;
}
bool
operator==(Rabbit const &r, int const &i) {
...
return ?;
}

Related

No instance of overloaded function "std::vector<_Ty, _Alloc>::erase [with _Ty=Enemy *, _Alloc=std::allocator<Enemy *>]" matches the argument list

for (auto enemy : this->enemies)
{
if (enemy->getHP() <= 0)
{
enemies.erase(enemy);
}
}
I have a vector enemies containing multiple of Enemy* elements and i want to erase an enemy if their hp is 0 or below
I write the code above and it gave me this error message:
No instance of overloaded function "std::vector<_Ty, _Alloc>::erase [with _Ty=Enemy *, _Alloc=std::allocator<Enemy *>]" matches the argument list
argument types are: (Enemy*)
object type is: std::vector<Enemy*,std::allocator<Enemy*>>
I assume that is not the right way to do it, so how?
Im new in stackoverflow and im still learning english so sorry if i made mistakes
EDIT:
It's my almost complete code:
struct enemyType
{
public:
int type;
sf::Vector2f pos;
}
std::vector<std::vector<enemyType>> enemyList = {
{
{ trashMonster, sf::Vector2f(5.f * 16, 18.f * 16) }
}
}
std::vector<Enemy*> enemies;
std::vector<Enemy*>* GetEnemy(int level)
{
for (int i = 0; i < enemyList[level].size(); i++)
{
switch (enemyList[level][i].type)
{
case trashMonster:
n_TrashMonster->setPosition(enemyList[level][i].pos);
enemies.emplace_back(n_TrashMonster);
break;
default:
std::cout << "Error to get an enemy\n";
break;
}
}
return &enemies;
}
//Code in different file
std::vector<Enemy*> enemies;
this->enemies = *GetEnemy(lvl);
for (auto enemy : this->enemies)
{
enemy->update(player->getCollisionBox());
//collision enemies to tilemap
collision::MapCollision(*this->map.getTilesCol(), *enemy);
if (enemy->getHP() <= 0)
{
enemies.erase(enemy);
}
}
Didn't include that because my code is a complete mess so I was afraid people won't get the point of my question and it's my first question here
The std::vector<T>::erase function does not have a erase(T a) overload. And if you want to remove elemennts from a vector you can't iterate over them like that. I suggest a convencional loop.
for (size_t i=0; i<this->enemies.size();++i){
if (this->enemies[i]->getHP()){
std::swap(enemies[i],enemies::back());
delete enemies::back();//Only if you don't free the space elsewere
enemies.pop_back();
}
}
Edit:
This will mess up the order of the vector. If you don't want that you can use erase(enemies.begin()+i) iinstead of swaping it back and removeing it
When using STL containers usually you don't even need an (explicit) for loop. Use std::remove_if like this
#include <vector>
#include <algorithm>
#include <iostream>
class Enemy
{
public:
// not explicit on purpose, so I can initalize vector more quickly (in real code you should have explicit constructors if they have one argument of a different type)
Enemy(int hp) :
m_hp{ hp }
{
};
int getHP() const noexcept
{
return m_hp;
}
int m_hp;
};
int main()
{
// create a test vector with enemies with given hitpoints
std::vector<Enemy> enemies{ 1,2,0,4,5,6,0,7,8 };
// boolean lambda function that determines if an enemy is dead.
auto enemy_is_dead = [](const Enemy& enemy) { return enemy.getHP() <= 0; };
// use remove_if (this will move all the items to be removed to the end
auto remove_from = std::remove_if(enemies.begin(), enemies.end(), enemy_is_dead );
// then shrink the vector
enemies.erase(remove_from, enemies.end());
for (const auto& enemy : enemies)
{
std::cout << enemy.getHP() << " ";
}
return 0;
}

Using STL functions with a struct?

I've recently read up on STL functions in C++. I understand the basic uses of the functions, but I am struggling getting them to use member variables of a struct.
I have this struct:
struct Apples
{
double weight; // oz
string color; // red or green
void print() const { cout << color << ", " << weight << endl; }
};
Basically, I insert Apples into a vector storing random weights and random color. Now, I want to use a count_if function to determine how many apples are greater than a given weight. I want to convert a function like this:
int cnt = 0;
for(auto it = crate.cbegin(); it != crate.cend(); ++it)
if(it->weight > toFind)
cnt++;
to a count_if() version (this does not work):
int cnt = count_if(crate.begin(), crate,end(), isGreater())
With isGreater() being like this:
void isGreater()
{
if(it->weight > toFind)
return it->weight > toFind;
}
What I don't understand about STL functions and a struct is how to use the member variables inside of the struct with the STL functions. I'm not sure what to pass inside of the STL function, either. Would it be better to use a lambda function in this case? If so, why?
Here is all the current code, if it doesn't make sense:
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <deque>
#include <string>
using namespace std;
struct Apples
{
double weight; // oz
string color; // red or green
void print() const { cout << color << ", " << weight << endl; }
};
void isGreater()
{
if(it->weight > toFind)
return it->weight > toFind;
}
int main()
{
srand(time(nullptr));
const double minWeight = 8.;
const double maxWeight = 3.;
cout << "Input crate size: ";
int size;
cin >> size;
vector <Apples> crate(size);
for(auto it = crate.begin(); it != crate.end(); ++it)
{
it->weight = minWeight + static_cast<double>(rand())/RAND_MAX*(maxWeight - minWeight);
it->color = rand() % 2 == 1 ? "green" : "red";
}
cout << "Enter weight to find: ";
double toFind;
cin >> toFind;
//this is what I want to convert to count if
int cnt = 0;
for(auto it = crate.cbegin(); it != crate.cend(); ++it)
if(it->weight > toFind)
cnt++;
std::count_if takes unary predicate as the third argument. In this case unary predicate is a function taking one object and returning true if object matches find criterion or false if not.
Since your criterion depends on toFind, it seems more laconic to use lambda capturing toFind:
int cnt = count_if(crate.begin(), crate.end(), [toFind](const Apple& apple) {
return it->weight > toFind;
});
If you want a standalone function, you can use:
bool isGreater(double toFind, const Apple& apple) {
return it->weight > toFind;
}
...
int cnt = count_if(crate.begin(), crate.end(),
std::bind(&isGreater, toFind, std::placeholders::_1));
Note, that you don't need to call function, you need to pass it:
int cnt = count_if(crate.begin(), crate,end(), isGreater())
// ^^ remove parentheses
you are not storing the apples in the vector.
you have to initialize inside a loop each apple and then store them in the vector.
crate.push_back(newApple).
so run a loop from 0 to size.
inside that loop initialize new apples and give them weights and colors
then push_back in vector:
for(int i = 0; i < size ++i)
{
apples newApple;
newApple.weight = ...;
newApple.color = ...;
crate.push_back(newApple);
}
This is usually accomplished by creating a "functor" class, a class whose objects can be called like a function. Each instance call hold the reference weight:
struct IsGreater {
double w;
IsGreater(double weight) : w{weight} {}
bool operator()(const Apples& A) const {
return A.weight > w;
}
};
Then we just need to create an instance of the class holding the reference weight and pass it to count_if:
const int count = std::count_if(crate.begin(), crate.end(), IsGreater(toFind));
You can avoid creating an explicit class using a lambda:
const int count = std::count_if(crate.begin(), crate.end(),
[=](const Apples& A) -> bool {
return A.weight > toFind;
});
Here the reference value toFind is captured by value.

Store dynamic amounts of data in a class C++

I have a class that stores data and within the class I have an array called 'position' that stores strings of 2 characters. Unfortunately, the amount of 2 character strings it should hold will vary:
class shipStatus
{
public:
char* name;
int x{};
char position[x][2]; // does not work
void setInfo(char name[], int x);
void displayStatus();
};
The setInfo function assigns a numerical value to x, which varies among objects. I would like the value of x to also dictate the length of the character array 'position'.
For example:
if x = 3 then
char position[3][2]; // the length of the second array is always 2
How can I make my code do this? If I try adding a variable as the parameters my code does not compile.
Here is my setInfo function:
void shipStatus::setInfo(char name[], int x)
{
name = name;
x = x;
}
Since this is C++, you should use the C++ facilities that are available to you. In your case, it would be std::string, std::vector, and std::array.
Below is an example using basically what your original shipStatus structure consisted of, and changing it to using the above mentioned constructs:
#include <string>
#include <array>
#include <vector>
#include <iostream>
class shipStatus
{
std::string name; // <-- Replaced char*
std::vector<std::array<char, 2>> position; // <-- A vector of an array that has a size of 2
public:
void setInfo(std::string n, int x);
void displayStatus();
void setPosition(size_t whichItem, char c1, char c2);
size_t getNumPositions() const;
};
void shipStatus::setInfo(std::string n, int x)
{
name = n;
position.resize(x); // <-- All we need to do is resize() to dynamically resize the vector
}
void shipStatus::displayStatus()
{
for (auto& p : position)
std::cout << p[0] << " " << p[1] << "\n";
}
void shipStatus::setPosition(size_t whichItem, char c1, char c2)
{
position[whichItem] = {c1, c2}; // <-- we set one item in the vector, and in
// that item, we set the [0] and [1] characters
}
size_t shipStatus::getNumPositions() const { return position.size(); }
int main()
{
shipStatus sStatus;
sStatus.setInfo("Ship 1", 3);
sStatus.setPosition(0, '4', '2');
sStatus.setPosition(1, 'a', 'b');
sStatus.setPosition(2, 'y', 'z');
std::cout << "Number of positions: " << sStatus.getNumPositions() << "\n";
sStatus.displayStatus();
}
Output:
Number of positions: 3
4 2
a b
y z
Note that we no longer need x as a member, since a std::vector knows its size already by calling the size() member function.
char **position;
position = (char **)malloc(sizeof(char *)*x);
for (int i=0; i<x; ++i)
{
position[i] = (char *)malloc(sizeof(char)*2);
}
This is a classic "C" way of doing it, will work in C++ too.
For a cleaner approach, we should use vectors/lists for this purpose.

Print struct inputs in alphabetic order C++

I want to print strings from a struct in alphabetic order, and I have got help from the thread How to alphabetically sort strings?, for the sorting. My problem is that when i run the compiler i get a sorted output but it includes a name from another struct. My code looks like this:
#include <iostream>
#include <set>
#include <algorithm>
#include <string>
using namespace std;
const int antalShops = 2;
const int antalWorkers = 5;
struct employ {
string workerName; int workerAge;
};
struct theMall{
string shopName; string shopType; int shopSize;
employ workerName; employ workerAge;
};
// Declaration of the structs
theMall Shops[antalShops] = {
{"GameStop","toy", 250,},
{"Frandsen", "cloth", 300,},
};
employ Workers[antalWorkers] = {
{"Andrea valente", 41},
{"Giovanni Pirolli", 25},
{"Marco Cipolli", 33},
{"Jensine Jensen", 19},
{"Andrea Jensen", 99},
};
// Functions for sorting and printing names
void print(const string& item) {
cout << item << endl;
}
void PrintWorkers(employ Workers[]) {
set<string> sortedWorkers;
for(int i = 0; i <= antalWorkers; ++i) {
sortedWorkers.insert(Workers[i].workerName);
}
for_each(sortedWorkers.begin(), sortedWorkers.end(), &print);
}
void PrintShops(theMall Shops[]) {
set<string> sortedShops;
for (int i = 0; i <= antalShops; ++i) {
sortedShops.insert(Shops[i].shopName);
}
for_each(sortedShops.begin(), sortedShops.end(), &print);
}
int main(int argc, const char * argv[])
{
PrintShops(Shops);
}
So I have the structs with workers and shops, but when I i try printing the shop names with the PrintShops(Shops) function i get the output:
Andrea Valente
Frandsen
GameStop
I have been looking through the code, but i can't find where the mistake is, anyone can see the error?
You go outside the bounds of the arrays in your loops
for (int i = 0; i <= antalShops; ++i) {
// Problem here ^^
The above loop will loop over indexes 0, 1 and 2, which is one to many for a two-entry array. This will lead to undefined behavior.
You have the same problem with the other sorting loop.

map<string, class> using and couting

I have few maps in my project, which I don't know, how to use or simple said: I don't know how to cout some info from map, which has class attached.
My code (item.h):
#ifndef ITEM_H
#define ITEM_H
class Item
{
public:
int level, durability, damage, armor, weight, grade, sex;
Item(int _level, int _durability, int _damage, int _armor, int _weight, int _grade, int _sex);
Item();
virtual ~Item();
protected:
private:
};
#endif // ITEM_H
code (item.cpp):
#include "include/Item.h"
Item::Item(int _level, int _durability, int _damage, int _armor, int _weight, int _grade, int _sex)
{
level = _level;
durability = _durability;
damage = _damage;
armor = _armor;
weight = _weight;
grade = _grade;
sex = _sex;
}
Item::Item(): level(0), durability(20), damage(0), armor(0), weight(1), grade(1), sex(0)
{
//dtor
}
Item::~Item()
{
//dtor
}
Code (main.cpp):
std::map<std::string, Item> item;
// level, durability, damage, armor, weight, grade, sex
item.insert(std::pair<std::string, Item>("Wooden Sword", Item(1, 19, 3, 0, 1, 1, 0)));
How to cout some info from the selected array from map? Tried searching in the google, etc. Can't find answer and explanation why do I get error if I try something like this:
cout << item["Wooden Sword"]["level"];
for what purpose you can use item.find("Wooden Sword"); ?
how does map work with classes? How can I select data from maps which have classes. Really thanks in advance. :) Hope I explained what I want and gave you everything that you need.
The index operator returns a reference to the data element, in your case it returns an Item& that you use as a normal structure:
std::cout << item["Wooden Sword"].level << '\n';
As for the find function it returns an iterator. If you don't know about iterators, study them first.
If you want a good reference, see e.g. this site.
Using item.find("Wooden Sword"); you get an iterator to the matching key/value pair element. If there is no key like "Wooden Sword" it returns end(item);. Use find if you don't know if the item exists and don't want to add one.
auto it = item.find("Wooden Sword");
if (it != end(item))
{
std::cout << it->level; // use the -> operator
// std::string key = it.first;
// Item& item = it.second;
}
else
std::cout << "There is no Wooden Sword";
The []operator returns a reference to the matching item. If there is no item associated with this key, a new item will be inserted at this position.
std::cout << item["Wooden Sword"].level;