Why std::iterator is not able to access the function - c++

When I try to access a function through iterator the compile tells that class ha no member with such name.
class Director
{
private:
std::string stdstrName;
public:
Director(std::string name): stdstrName(name){ }
void setName(std::string name) { stdstrName = name; }
std::string getName() { return stdstrName; }
};
int main(){
std::vector<std::shared_ptr<Director>> stdVecDir;
std::shared_ptr<Director> pointer1 = std::make_shared<Director>("Director1");
std::shared_ptr<Director> pointer2 = std::make_shared<Director>("Director2");
std::shared_ptr<Director> pointer3 = std::make_shared<Director>("Director3");
stdVecDir.push_back(pointer1);
stdVecDir.push_back(pointer2);
stdVecDir.push_back(pointer3);
auto it = std::find(stdVecDir.begin(), stdVecDir.end(), [](std::shared_ptr<Director> dir) { return dir->getName() == "Director2"; });
if (it != std::end(stdVecDir))
std::cout << *it->getName(); // compiler complains that getName() is not a member of class Director
}
Why does the compile complains that getName() is not a member of class Director.

You are using std::find(), but it's used with values, not with callables, hence you need to use std::find_if.
And you are using your iterator to access to a member function of an object pointed to by a pointer which is pointed to by an iterator, hence you have two dereferences, not one.
The following is a correction
auto it = std::find_if(stdVecDir.begin(), stdVecDir.end(),
[](std::shared_ptr <Director> dir) { return dir->getName() == "Director2"; });
if (it != std::end(stdVecDir))
std::cout << it->get()->getName(); //or (*it)->getName();

If im not mistaken you need to change it to (*it)-> to access Director from iterator and shared_ptr. With *it-> you are accessing shared_ptr from the iterator.

Related

std::find return a class that I can't acesses functions

I come from C/C# language and now I'm trying to learn about C++ and his standards functions.
Now, I'm creating a class called IMonsterDead. I will have a std::vector<IMonsterDead*> with N monsters.
Example:
class IMonsterDead {
public:
IMonsterDead(int Id)
{
this->_Id = Id;
}
virtual void OnDead() = 0;
int Id() const {
return _Id;
}
private:
int _Id;
};
One class which implements that class:
class MonsterTest : public IMonsterDead {
public:
MonsterTest(int generId)
: IMonsterDead(generId)
{
}
virtual void OnDead()
{
std::cout << "MonsterTesd died" << std::endl;
}
};
Ok, if I access directly everything works fine. But I'm trying to use std::find.
Full program test:
int main()
{
std::vector<IMonsterDead*> monsters;
for (int i = 0; i < 1000; i++)
{
monsters.emplace_back(new MonsterTest(1000 + i));
}
int id = 1033;
std::vector<IMonsterDead*>::iterator result = std::find(monsters.begin(), monsters.end(), [id]( IMonsterDead const* l) {
return l->Id() == id;
});
if (result == monsters.end())
std::cout << "Not found" << std::endl;
else
{
// Here I want to access OnDead function from result
}
return 0;
}
So I need to access OnDead function from result but I can't. Intellisense doesn't show anything for me. The result exists.
How can I access that function? Have another better way to do that?
You need to use std::find_if() instead of std::find(). std::find() is for finding an element with a specific value, so you have to pass it the actual value to find, not a user_defined predicate. std::find_if() is for finding an element based on a predicate.
Either way, if a match is found, dereferencing the returned iterator will give you a IMonsterDead* pointer (more accurately, it will give you a IMonsterDead*& reference-to-pointer). You need to then dereference that pointer in order to access any members, like OnDead().
You are also leaking memory. You are not delete'ing the objects you new. And when dealing with polymorphic types that get deleted via a pointer to a base class, the base class needs a virtual destructor to ensure all derived destructors get called properly.
With that said, you are clearly using C++11 or later (by the fact that you are using vector::emplace_back()), so you should use C++11 features to help you manage your code better:
You should use std::unique_ptr to wrap your monster objects so you don't need to delete them manually.
You should always use the override keyword when overriding a virtual method, to ensure you override it properly. The compiler can catch more syntax errors when using override than without it.
You should use auto whenever you declare a variable that the compiler can deduce its type for you. Especially useful when dealing with templated code.
Try something more like this:
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
class IMonsterDead {
public:
IMonsterDead(int Id)
: m_Id(Id)
{
}
virtual ~IMonsterDead() {}
virtual void OnDead() = 0;
int Id() const {
return m_Id;
}
private:
int m_Id;
};
class MonsterTest : public IMonsterDead {
public:
MonsterTest(int generId)
: IMonsterDead(generId)
{
}
void OnDead() override
{
std::cout << "MonsterTest died" << std::endl;
}
};
int main()
{
std::vector<std::unique_ptr<IMonsterDead>> monsters;
for (int i = 0; i < 1000; i++)
{
// using emplace_back() with a raw pointer risks leaking memory
// if the emplacement fails, so push a fully-constructed
// std::unique_ptr instead, to maintain ownership at all times...
monsters.push_back(std::unique_ptr<IMonsterDead>(new MonsterTest(1000 + i)));
// or:
// std::unique_ptr<IMonsterDead> monster(new MonsterTest(1000 + i));
// monsters.push_back(std::move(monster));
// or, if you are using C++14 or later:
// monsters.push_back(std::make_unique<MonsterTest>(1000 + i));
}
int id = 1033;
auto result = std::find_if(monsters.begin(), monsters.end(),
[id](decltype(monsters)::value_type &l) // or: (decltype(*monsters.begin()) l)
{
return (l->Id() == id);
}
// or, if you are using C++14 or later:
// [id](auto &l) { return (l->Id() == id); }
);
if (result == monsters.end())
std::cout << "Not found" << std::endl;
else
{
auto &monster = *result; // monster is 'std::unique_ptr<IMonsterDead>&'
monster->OnDead();
}
return 0;
}
Iterators are an interesting abstraction, in this case to be reduced to pointers.
Either you receive the pointer to the element or you get an invalid end.
You can use it as a pointer: (*result)->func();
You can also use it to create a new variable:
IMonsterDead &m = **result;
m.func();
This should give the same assembly, both possible.

Strange issue when iterating a STL set<CStudent> in C++

I have class CStudent and class CStudentGroup which has one member set<CStudent>. I populate the set of an object from the class CStudentGroup. I want to iterate this set and print via the getter of the CStudent class the points of all the students in the set. I do this by assigning the set to a new one. Then I iterate the set with an iterator it. However the compiler gives an error *the object has type qualifiers that are not compatible with the member function CStudent::getP; object type is const CStudent* I would like to ask how can I do this? Thank you in advance.
#include <iostream>
#include <string>
#include <set>
using namespace std;
class CStudent {
string m_strFN;
int m_iPoints;
public:
void setP(int p) {
m_iPoints = p;
}
void setFN(string f) {
m_strFN = f;
}
int getP() {
return m_iPoints;
}
string getFN() {
return m_strFN;
}
CStudent() {
m_strFN = "123456789";
m_iPoints = 70;
}
CStudent(const CStudent& stud) {
m_strFN = stud.m_strFN;
m_iPoints = stud.m_iPoints;
};
CStudent(int p) {
m_iPoints = p;
}
};
class CStudentGroup {
set<CStudent> m_setStudents;
public:
CStudentGroup(const CStudentGroup& grp) {
m_setStudents = grp.m_setStudents;
};
CStudentGroup(set<CStudent> st) {
m_setStudents = st;
}
CStudentGroup() {
CStudent s1(50), s2, s3(s2);
m_setStudents.insert(s1);
m_setStudents.insert(s2);
m_setStudents.insert(s3);
}
set<CStudent> gets() {
return m_setStudents;
}
};
int main()
{
CStudentGroup group;
set<CStudent> stt = group.gets();
for (set<CStudent>::iterator it = stt.begin(); it != stt.end(); it++) {
cout << it->getP() << endl;
}
}
std::set stores keys as constant value, as a change of a key can be a cause of change to its position in red-black tree (typical std::set implementation).
In other words, your CStudent object are considered const or unchangeable.
It's possible to problem here using std::set::const_iterator as a type of iterator inside the loop in combination with std::set::cbegin() and std::set::cend() calls.
Another possible solution is to use foreach-loop:
for (CStudent const& student : stt)
std::cout << student.getP() << '\n';
Moreover, you would need to change CStudent::getP() declaration to be a constant method.
Objects inside a std::set are always const. That is to protect them, in case you decide you change any key field, the sorting order changes and the set invariant is broken.
So basically the set<CStudent>::iterator is a const_iterator and you get a const CStudent& reference. Since your CStudent::getP is not a const member function, you cannot use it.
Solution, make it const:
int getP() const {
return m_iPoints;
}
Naturally, you want to mark as const any function that does not change the contents of your object, not only the ones std::set requires you to do so. This is sometimes called const-correctness and is always a good practice.

C++ calling a function pointer stored in a struct in a map

I am getting the error term does not evaluate to a function taking 1 arguments when trying to call a function pointer.
The function pointer is stored in a struct. The struct is then stored in a map.
Definition:
typedef void (CLIOptions::*OptionHandler)(QString);
struct OptionDefinition {
QString name;
QString description;
QString type;
OptionHandler handler;
};
typedef std::map<QString, OptionDefinition> CLIOptionMap;
I initialise the map like this:
CLIOptionMap optionMap =
{
{
QString("set-tcp-host"),
{
QString("set-tcph"),
QString("Set the TCP server host address"),
QString("string"),
&CLIOptions::setTcpHost
}
},
// etc...
}
The problem occurs when I try to iterate through the map and call the handler:
for (it = optionMap.begin(); it != optionMap.end(); ++it) {
QString value = /*snip...*/
(it->second.handler)(value)
}
What is my problem here?
Your problem is that you don't have a function pointer, you have a pointer to member function, and they are very different beasts. A pointer-to-member-function isn't even a pointer in general (it has to be able to handle pointer to a virtual function in a second virtual base class!)
Given you have a pmf, you need an object to invoke the pmf on. So something like:
for (it = optionMap.begin(); it != optionMap.end(); ++it) {
QString value = /*snip...*/
const auto pmf = it->second.handler;
(mOptionHandler.*pmf)(value);
}
actually, if you going to use C++11 auto, you can also use the foreach loop:
for (const auto& option : optionMap) {
const auto pmf = option.handler;
(mOptionHandler.*pmf)(option.value);
}

Get class pointer from a vector via function argument

I have a class
class MyClass {
public:
string name;
};
I have a class manager, with a vector of said classes.
class MyManager {
public:
vector<MyClass> classes;
};
I want to pull out a class from the vector by looping and matching with the name
int MyManager::FindMyClass(string name, MyClass *clazz){
vector<MyClass>::iterator it;
for(it = classes.begin(); it != classes.end(); ++it){
if(it->name == name){
// if i cout here, i see the cout, so i did find it..
*clazz = *it;
return 1;
}
}
return 0;
}
int main() {
MyClass *myClass;
int result = myManager.FindMyClass("bob", *myClass);
}
I know for a fact that the object is matched properly (see the comment). I've tried every combination of pointers, double pointers, references, and get every error from unknown conversions to invalid pointers. I am just not sure what I have to do to get a reference to the class stored in the vector.
Change the argument type to MyClass*& so you can return a pointer to the object found in the vector.
int MyManager::FindMyClass(string name, MyClass* &foundPointer)
MyClass *myClass;
int result = myManager.FindMyClass("bob", myClass);
Once you find the match, you can get a pointer to the object using
foundPointer = &(*it);
I would also recommend changing the return type to bool.
Here's an updated version of the function:
bool MyManager::FindMyClass(string name, MyClass* &foundPointer)
{
vector<MyClass>::iterator it = classes.begin();
for(; it != classes.end(); ++it)
{
if(it->name == name)
{
foundPointer = &(*it);
return true;
}
}
return false;
}
Suggested second improvement:
Change the return type to MyClass*. Return a pointer to the found object or nullptr if none is found.
MyClass* MyManager::FindMyClass(string name)
{
...
}
Suggested third improvement:
Use the standard library function std::find_if and a lambda expression to simplify your function.
auto it = std::find_if(classes.begin(),
classes.end(),
[&](const MyClass &c) { return (c.name == name); });
The updated function:
MyClass* MyManager::FindMyClass(string name)
{
auto it = std::find_if(classes.begin(),
classes.end(),
[&](const MyClass &c) { return (c.name == name); });
if ( it != classes.end() )
{
return &(*it);
}
return nullptr;
}

Class member to point to a specific node member of an STL list?

How can I store a pointer to the name of a person's spouse as a private member of the person's class?
For example, say I have the following code:
#include <iostream>
#include <list>
using namespace std;
class person
{
private:
string name;
string *spouse;
public:
void setName(string tempName) { name = tempName; }
void setSpouse(string &tempSpouse) { spouse = &tempSpouse; } // ERROR HERE?
string getName() { return name; }
string getSpouse() { return spouse; } // ERROR HERE?
};
int main()
{
person entry;
list<person> personList;
list<person>::iterator itr1, itr2;
/* Adding two people/nodes to the linked list. */
entry.setName("John Doe");
personList.push_back(entry);
entry.setName("Tina Doe");
personList.push_back(entry);
/* Attempting to assign Tina Doe as John Doe's spouse. */
for (itr1 = personList.begin(); itr1 != personList.end(); itr1++)
{
if (itr1->getName() == "John Doe")
{
for (itr2 = personList.begin(); itr2 != personList.end(); itr2++)
{
if (itr2->getName() == "Tina Doe")
{
itr1->setSpouse(itr2->getName()); // ERROR HERE?
}
}
}
}
/* Displaying all Names with Spouses afterwards. */
for (itr1 = personList.begin(); itr1 != personList.end(); itr1++)
{
cout << "Name: " << itr1->getName() << " | Spouse: " << itr1->getSpouse() << endl;
}
return 0;
}
I am unable to assign the address of the spouses name to the pointer member in the class. I've noted in the comments where I believe the errors might be.
You can view the code and errors here: https://ideone.com/4CXFnt
Any help will be greatly appreciated. Thank you.
getName returns a temporary std::string (copy of the name variable), and the compiler is trying to save you from referencing a part of memory that will be soon deleted. This error has nothing to do with lists - to fix it you need to either store copies in spouse variable (which will result in storing same data in multiple places) or return a reference in getName. You can also consider creating another accessor (private one), but it's ugly.
I'd recommend storing copies, but if references/pointes are really required then it's sufficient to modify lines:
string getName() { return name; }
string getSpouse() { return spouse; } // ERROR HERE?
to
string& getName() { return name; }
string getSpouse() { return *spouse; } // ERROR HERE?
however, for consistency I'd recommend:
string& getName() { return name; }
string& getSpouse() { return *spouse; } // ERROR HERE?
You have two problems. The first is easy to fix: getSpouse doesn't return the right type. You should write
string getSpouse() { return *spouse; } // Derefencing the pointer and returning a string
// or
string* getSpouse() { return spouse; } // Returning the pointer
The second problem is more subtle. When you write: itr2->getName() , you only have a value (a copy of itr2->name): you do not store a variable that you will be able to change, so you cannot make a reference to it.
There is no easy way you can have a pointer in a class instance pointing the a private variable of another instance. I guess you should question the way you are doing things and create a pointer to the person and not to the name (remember pointer are cheap, they are just memory locations).
class person
{
private:
string name;
person *spouse;
public:
void setSpouse(person *_spouse) { spouse = _spouse; }
person* getSpouse() { return spouse; }
string getSpouseName() { return spouse->getName(); }
};
This work, but still it is not safe: if spouse is destroyed and try to access it from person, you will run into deep problems… (note that you had the same problem with the string).
So what is the fix? Well, if you only want B to be the spouse of A, you can create B first and make a reference in A with a constructor. But if you want B to be the spouse of A and A the spouse of B, you have to use either the unsafe trick above and be careful, or stock the spouses' list outside the class.