Changing a classes member through an iterator - c++

I'm learning C++ and can't get my head around this problem:
I have a simple class A
class A {
private:
int ival;
float fval;
public:
A(int i = 0, float f = 0.0) : ival(i), fval(f) { }
~A(){ }
void show() const {
cout << ival << " : " << fval << "\n";
}
void setVal(int i) {
ival = i;
}
//const getters for both ival and fval
//used for the default "lesser"
friend bool operator<(const A& val1, const A& val2) {
return val1.ival < val2.ival ? true : false;;
}
}
Then I have a regular set<A> myset that gets filled with insert(A(2, 2.2)); in a loop.
Iterating to get all the values is not a problem but I want to modify the value within this iteration:
for(set<A>::iterator iter = set3.begin(); iter != set3.end(); iter++) {
iter->setVal(1);
}
I assume that this should be doable, like you would do it in Java within a foreach loop. When compiling I get error: passing ‘const A’ as ‘this’ argument of ‘void A::setVal(int)’ discards qualifiers.
Looking at the sources of the STL set, i see that begin() is only available as a const method and I think this might be the problem. Messing around with const on the setVal() method got always the same error and wouldn't make much sense since I want to modify the value of A.
Is this the wrong approach of changing a bunch of A's values with a loop?

The STL set does not let you change values it stores. It does that by returning a copy of the object through the iterator (not the actual one in the set).
The reason that set does this is because it's using < to order the set and it doesn't want to remake the entire tree every time you dereference the iterator, which it would have to do, since it doesn't know if you changed anything that changes the ordering.
If you need to update the set<>, remove the old value and add in a new one.
EDIT: just checked source to SGI STL and it says this:
typedef typename _Rep_type::const_iterator iterator;
So, a set::iterator is just a set::const_iterator

From this page, it seems that begin() exists as well as a non-const method.
Perhaps your set is passed into the method as a const reference ?
EDIT
The referenced page is wrong. As Scharron states, there is no non-const begin() (or end() for that matter) method for ordered containers.
I will inform the website about their mistake (it's not the first they made ;))

Related

c++ "error:passing 'const std::vector<int>' as 'this' argument discards qualifiers [-fpermissive]"

I am trying to implement this POMDP solver with given examples for my decision making problem and I followed the documentation in the repository to build different relevant classes and functions in the header file
class SimpleState: public State {
public:
int position;
int context;
int time;
SimpleState();
SimpleState(int _position, int _context, int _time) :
rat_position(_position),
context(_context),
time(_time) {
}
SimpleState(int _state_id);
~SimpleState();
std::string text() const;
};
SimpleState::SimpleState() {
}
class StarMazeProblem : public DSPOMDP,
public MDP {
protected:
std::vector<std::vector<std::vector<State> > > transition_probabilities_; //state, action, [state, weight]
mutable MemoryPool<SimpleState> memory_pool_;
std::vector<SimpleState*> states_;
mutable std::vector<ValuedAction> mdp_policy_;
public:
enum {CENTER = 0, RIGHT = 1, LEFT = 2};
public:
StarMazeProblem();
int NumStates() const;
void ComputeDefaultActions(std::string type) const;
ParticleUpperBound* CreateParticleUpperBound(std::string name = "DEFAULT") const;//?
ScenarioUpperBound* CreateScenarioUpperBound(std::string name = "DEFAULT",
std::string particle_bound_name = "DEFAULT") const;
ScenarioLowerBound* CreateScenarioLowerBound(std::string name = "DEFAULT",
std::string particle_bound_name = "DEFAULT") const;
}
and in the starmaze.cpp file the relevant lines are
int StarMazeProblem::NumStates() const {
return CONTEXT * POSITIONS * TIME;
}
void StarMazeProblem::ComputeDefaultActions(string type) const {
cerr << "Default action = " << type << endl;
if (type == "MDP") {
const_cast<StarMazeProblem*>(this)->ComputeOptimalPolicyUsingVI();
int num_states = NumStates();
default_action_.resize(num_states);
double value = 0;
for (int s = 0; s < num_states; s++) {
default_action_[s] = policy_[s].action;
value += policy_[s].value;
}
} else {
cerr << "Unsupported default action type " << type << endl;
exit(0);
}
}
ScenarioLowerBound* StarMazeProblem::CreateScenarioLowerBound(string name,
string particle_bound_name="DEFAULT") const {
const DSPOMDP* model = this;
const StateIndexer* indexer = this;
const StatePolicy* policy = this;
ScenarioLowerBound* bound = NULL;
if (name == "TRIVIAL" ) {
bound = new TrivialParticleLowerBound(this);
} else if (name == "RANDOM") {
bound = new RandomPolicy(this,
CreateParticleLowerBound(particle_bound_name));
} else if (name == "MODE" || name == "DEFAULT") {
ComputeDefaultActions("MDP");
bound = new ModeStatePolicy(model, *indexer, *policy,
CreateParticleLowerBound(particle_bound_name));
} else {
cerr << "Unsupported scenario lower bound: " << name << endl;
exit(1);
}
return bound;
}
here I got the following error for the above code:
src/starmaze.cpp:301:36: error: passing 'const std::vector<int>' as 'this' argument discards qualifiers [-fpermissive]
default_action_.resize(num_states);
^
In file included from /opt/local/include/gcc7/c++/vector:64:0,
from ../../../include/despot/interface/lower_bound.h:4,
from ../../../include/despot/core/builtin_lower_bounds.h:4,
from src/starmaze.h:3,
from src/starmaze.cpp:1:
/opt/local/include/gcc7/c++/bits/stl_vector.h:689:7: note: in call to 'void std::vector<_Tp, _Alloc>::resize(std::vector<_Tp, _Alloc>::size_type) [with _Tp = int; _Alloc = std::allocator<int>; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'
resize(size_type __new_size)
^~~~~~
I have basic c++ knowledge and I could not figure out the reason for the error since I followed the examples. Any suggestion?
Important to know before reading
I've looked over this code several times, and cannot find where default_action_ is declared as a const std::vector<int>. This leads me to assume (based on the fact that the compiler recognizes it as such) that the variable is declared in one of the parent classes: public DSPOMDP, public MDP.
You are trying to modify a const vector<int>
This issue is pretty straight-forward.
You cannot change a const std::vector<Ty>
The whole point of making something const is to prevent the programmer from changing it.
Whatever you are trying to do here, you should consider copying the vector data into a new vector, and make the changes to it in the copy.
Okay so let's say you decided to do the naughty deed and remove const from the *.h file wherever it may be. You'll still have an issue (see below)
You are trying to modify the class instance from a const method
This is if you removed const from the vector
The default_action_.resize(num_states); is going to cause the compiler to yell at you.
The issue here is now your method StarMazeProblem::ComputeDefaultActions() is specified as const. This means you cannot change your class instance in any way from within that function/class method. What you are doing is modifying the default_action_ vector by calling resize() on it. This inevitably changes the class instance which is forbidden because your method is declared const and your default_action_ vector is declared somewhere in your class hierarchy. Additionally you are changing the vector directly by assigning it to an rvalue (See code below)
int num_states = NumStates();
default_action_.resize(num_states); // <---- default_action_ cannot be modified
for (int s = 0; s < num_states; s++) {
default_action_[s] = policy_[s].action; // <---- default_action_ cannot be modified
value += policy_[s].value;
}
Solution
You need a copy of the vector you are trying to modify. Going into h files and changing them without knowing 100% exactly what you are doing is never the answer.
Making a temporary vector in the class method, copying the data to it, and manipulating the copy is completely allowed and will not give you any compiler errors; however, this approach may completely avoid your official goal. It looks like you are really trying to change the default_action_ vector and that almost seems like your true goal here.
It's hard for me to give you a definitive answer on this specific topic. Mainly because I do not know what this entire class hierarchy does or what it is for exactly (I briefly looked over the code in the link provided.)
What you'll need to do ultimately is determine why you are trying to change that vector, and why it was made const by the original developers. Then you can determine if there is a better way other than explicitly removing the constness of the vector.
If you are still trying to make changes to the class instance read below
This solution part has to do with the qualifier of the class you are dealing with.
You can either remove the constant qualifier from the class method if it is absolutely necessary to make changes to the class instance from within that method.
OR
Find an alternative method for what you are trying to do, and break it up into multiple method calls on the class instance.
You've made ComputeDefaultActions() const-qualified, but there is very little that says that it should be.
It calls ComputeOptimalPolicyUsingVI() which is not const-qualified since it changes the internal state of *this. You've worked around that by casting away const on this - but that function resizes policy_ to also be NumStates() big which ComputeDefaultActions() depends on - otherwise the internal loop would be very dangerous.
It changes default_action_ by resizeing it and by assigning its elements. This may be a mistake, if the computation made is actually meant to not change *this but ... (see next bullet)
It doesn't return anything. Everything the function does is to change the internal state of *this. The computed value is just dropped at the end of the calculation and it's not even used internally in the function. Either make the function return value or remove value completely.
Suggested changes:
#include <algorithm>
#include <stdexcept>
void StarMazeProblem::ComputeDefaultActions(string type) { // non-const
cerr << "Default action = " << type << endl;
if (type == "MDP") {
ComputeOptimalPolicyUsingVI(); // no casting needed
default_action_.resize(policy_.size()); // dependency made clear
// Use the standard transform function:
std::transform(policy_.begin(), policy_.end(), default_action_.begin(),
[](const auto& p) { return p.action; });
} else {
// friendlier than exit():
throw std::runtime_error("Unsupported default action type " + type);
}
}

Creating a map with custom class as key, from unordered map. However elements not useable due to const

I'm trying to to map elements from my unordered map that contain to a map that contain an Class T, int and sortBy being the compare class.
However I can't perform any functions on any elements in the map because they are all const. I have no idea how to make them not const, or why they were const in the first place.
unordered_map < int,LibrarySong> Library;
map < LibrarySong,int, LibrarySong:: sortbyPlay> sortedLibrary;
for(unordered_map<int,LibrarySong>:: iterator it = Library.begin(); it != Library.end(); ++it){
sortedLibrary.insert(pair <LibrarySong,int> ( it->second, it->first));
}
for(map < LibrarySong,int>::iterator it=sortedLibrary.begin(); it != sortedLibrary.end(); ++it){
cout << it->first.print() << endl; //Cant do anything because it is const
};
The LibrarySongs header has the struct
struct sortbyPlay {
bool operator() (const LibrarySong &lhs ,const LibrarySong &rhs) const{
return lhs.numPlay < rhs.numPlay;
}
};
I've tried removing the const in the function but that doesn't work either?
Maybe there is a better way to do this, I'm not sure. I was thinking of vectors and then using sort(), but I needed both values(probably could use a pair?) however I'm not sure if the const problem would still persist as well.
The const-ness of the map's comparator is completely irrelevant.
cout << it->first.print() << endl; //Cant do anything because it is const
The map's keys, the first values, are always const. As such, the print() method in the class must be a const method. Although your question completely fails to provide a minimal, complete, and verifiable example, it's obvious that your print() method must be a const method, and it is not.
A class method, such as print(), does not need to modify its object. At least not in a sane class design. Therefore, you should not have any issues declaring your print() method as a const class method.

How to iterate through all elements of set C++

[UPDATE: My problem is solved! Lots of thanks to Mike Seymour and Niall and all you guys!]
My code has errors in the for loop and I do not know how to fix it :(
MyClass::ITECH7603Class(set<Student>* students) {
/* Initialize dynamically the group field */
group = new map<string, Student>();
for (set<Student>::iterator it = students->begin(); it != students->end(); it++) {
addStudent(it);
}
}
void MyClass::addStudent(Student* studentPtr) {
string fullName = studentPtr->getName() + " " + studentPtr->getSurname();
group->insert(pair<string, Student>(fullName, *studentPtr));
}
So the main idea is to loop through all students in the set, and add each student into a map group. Any help? Thank you very much!
for (set<Student>::iterator it = students->begin; it != students->end; it++) {
addStudent(it);
}
should be:
for (set<Student>::iterator it = students->begin(); it != students->end(); it++) {
//^^ //^^
addStudent(it);
}
addStudent takes a pointer, while it is an iterator, so can't be passed directly.
You should change addStudent to take either a value or a pointer/reference to const:
// option 1
void addStudent(Student);
addStudent(*it);
// option 2
void addStudent(Student const &);
addStudent(*it);
// option 3
void addStudent(Student const *);
addStudent(&*it);
If, as you say in a comment, you must leave it taking a mutable pointer, then you'll need some grotesquery to deal with the fact that elements of the set are immutable:
// nasty option
addStudent(const_cast<Student*>(&*it));
// slightly less nasty option
Student copy = *it;
addStudent(&copy);
Beware that the first option will give undefined behaviour if the function uses the dodgy pointer to make any modification to the Student object stored in the set. The second makes a temporary copy, which can be modified without breaking the set. This is fine as long as addStudent only stores a copy of the object passed to it, not the pointer itself, which will become invalid when copy is destroyed.
In c++11 you can use range for sytax:
for (const auto &student : *students)
{
addStudent(it);
}
Then change addStudent function signature to accept reference:
void MyClass::addStudent(const Student &student) {
While you've gotten answers that "fix" your code to the extent of compiling and producing results that you apparently find acceptable, I don't find them very satisfying in terms of code style. I would do this job rather differently. In particular, my code to do this wouldn't have a single (explicit) loop. If I needed to do approximately what you're asking for, I'd probably use code something like this:
std::pair<std::string, Student> make_mappable(Student &stud) {
return std::make_pair(stud.getName() + " " + stud.getSurName(), stud);
}
std::map<std::string, Student> gen_map(std::set<Student> const &input) {
std::map<std::string, Student> ret;
std::transform(input.begin(), input.end(),
std::inserter(ret, ret.end()),
make_mappable);
return ret;
}
There definitely would not be any new in sight, nor would there be any passing a pointer to a Student.
OTOH, since the data you're using as the key for your map is data that's already in the items in the set, it may more convenient all around to continue to use a set, and just specify a comparison function based on the student's name:
struct by_given_name {
bool operator()(Student const &a, Student const &b) const {
if (a.getName() < b.getName())
return true;
if (b.getName() < a.getName())
return false;
return a.getSurName() < b.getSurName();
}
};
std::set<Student, by_given_name> xform(std::set<Student> const &in) {
return std::set<Student, by_given_name>{in.begin(), in.end()};
}
For what its worth, a Live Demo of the latter.
Whether the latter is practical will typically depend on one other factor though: your ability to create a Student from only a name/surname. If you can't do that, searching by name will be inconvenient (at best), so you'd want to use a map.
I realize this probably isn't much (if any) help in completely what's apparently home-work for a class--but even if your class prevents you from actually turning in decent code, it seems worthwhile to me to at least try to learn to write decent code in addition to what it requires. If you do pass the class and get a job writing code, you'd probably rather your coworkers didn't want to hurt you.

how to get a non-const value from a vector

If I have a class:
class T{
public:
int Id;
//some methods, constructor..
}
and in other class:
vector<T> collection;
and want to a write a method:
T& getValue(int Id){
//scanning the vector till i find the right value
}
The problem is that scanning the vector through iterator always give a const value so I get an error about qualifiers. So how do I get a value from a vector? but Not a const one.
EDIT: According to the Answers I tried to so something like this:
Group& Server::findGroup(unsigned int userId) const{
for(auto iterator=groups.begin();iterator!=groups.end();++iterator){
if(iterator->isInGroup(userId)){
return (*iterator);
}
}
//throw exception here
}
the definition of groups:
vector groups;
This is exactly the same example I gave at first but now T is Group.
The following code should give you a non-const iterator and work fine:
for(vector<T>::iterator i = collection.begin();
i != collection.end(); ++i) {
if(Id != i->Id)
continue;
// Matching ID! do something with *i here...
break;
}
If this doesn't help, please explain in more detail what is broken.
The problem here is the const in your declaration:
Group& Server::findGroup(unsigned int userId) const //<==THIS
That means that this is a const Server*, and thus everything in it is const as well, including groups. Which means that groups.begin() will return a const_iterator instead of an iterator.
One thing you can do (might not be a good idea; you need to be really sure!) would be to mark groups as mutable, which lets it be changed even if its enclosing object is const:
mutable vector<T> groups;
Doing this will make groups.begin() return a regular iterator.
But I would instead ask you to reevaluate why this method is declared const at all, since you're returning part of the object in a form that can be changed and thus you're not really honoring const.

Simple C++ getter/setters

Lately I'm writing my getter and setters as (note: real classes do more things in getter/setter):
struct A {
const int& value() const { return value_; } // getter
int& value() { return value_; } // getter/setter
private:
int value_;
};
which allows me to do the following:
auto a = A{2}; // non-const object a
// create copies by "default" (value always returns a ref!):
int b = a.value(); // b = 2, is a copy of value :)
auto c = a.value(); // c = 2, is a copy of value :)
// create references explicitly:
auto& d = a.value(); // d is a ref to a.value_ :)
decltype(a.value()) e = a.value(); // e is a ref to a.value_ :)
a.value() = 3; // sets a.value_ = 3 :)
cout << b << " " << c << " " << d << " " << e << endl; // 2 2 3 3
const auto ca = A{1};
const auto& f = ca.value(); // f is a const ref to ca.value_ :)
auto& g = ca.value(); // no compiler error! :(
// g = 4; // compiler error :)
decltype(ca.value()) h = ca.value(); // h is a const ref to ca.value_ :)
//ca.value() = 2; // compiler error! :)
cout << f << " " << g << " " << h << endl; // 1 1 1
This approach doesn't allow me to:
validate the input for the setter (which is a big BUT),
return by value in the const member function (because I want the compiler to catch assignment to const objects: ca.value() = 2). Update: see cluracan answer below.
However, I'm still using this a lot because
most of the time I don't need that,
this allows me to decouple the implementation details of my classes from their interface, which is just what I want.
Example:
struct A {
const int& value(const std::size_t i) const { return values_[i]; }
int& value(const std::size_t i) { return values_[i]; }
private:
std::vector<int> values_;
// Storing the values in a vector/list/etc is an implementation detail.
// - I can validate the index, but not the value :(
// - I can change the type of values, without affecting clients :)
};
Now to the questions:
Are there any other disadvantages of this approach that I'm failing to see?
Why do people prefer:
getter/setters methods with different names?
passing the value as a parameter?
just for validating input or are there any other main reasons?
Generally using accessors/mutators at all is a design smell that your class public interface is incomplete. Typically speaking you want a useful public interface that provides meaningful functionality rather than simply get/set (which is just one or two steps better than we were in C with structs and functions). Every time you want to write a mutator, and many times you want to write an accessor first just take a step back and ask yourself "do I *really* need this?".
Just idiom-wise people may not be prepared to expect such a function so it will increase a maintainer's time to grok your code.
The same-named methods are almost the same as the public member: just use a public member in that case. When the methods do two different things, name them two different things.
The "mutator" returning by non-const reference would allow for a wide variety of aliasing problems where someone stashes off an alias to the member, relying on it to exist later. By using a separate setter function you prevent people from aliasing to your private data.
This approach doesn't allow me to:
return by value in the const member function (because I want the compiler to catch assignment to const objects ca.value() = 2).
I don't get what you mean. If you mean what I think you mean - you're going to be pleasantly surprised :) Just try to have the const member return by value and see if you can do ca.value()=2...
But my main question, if you want some kind of input validation, why not use a dedicated setter and a dedicated getter
struct A {
int value() const { return value_; } // getter
void value(int v) { value_=v; } // setter
private:
int value_;
};
It will even reduce the amount typing! (by one '=') when you set. The only downside to this is that you can't pass the value by reference to a function that modifies it.
Regarding your second example after the edit, with the vector - using your getter/setter makes even more sense than your original example as you want to give access to the values (allow the user to change the values) but NOT to the vector (you don't want the user to be able to change the size of the vector).
So even though in the first example I really would recommend making the member public, in the second one it is clearly not an option, and using this form of getters / setters really is a good option if no input validation is needed.
Also, when I have classes like your second type (with the vector) I like giving access to the begin and end iterators. This allows more flexibility of using the data with standard tools (while still not allowing the user to change the vector size, and allowing easy change in container type)
Another bonus to this is that random access iterators have an operator[] (like pointers) so you can do
vector<int>::iterator A::value_begin() {return values_.begin();}
vector<int>::const_iterator A::value_begin()const{return values_.begin();}
...
a.value_begin()[252]=3;
int b=a.value_begin()[4];
vector<int> c(a.value_begin(),a.value_end())
(although it maybe ugly enough that you'd still want your getters/setters in addition to this)
REGARDING INPUT VALIDATION:
In your example, the assignment happens in the calling code. If you want to validate user input, you need to pass the value to be validated into your struct object. This means you need to use member functions (methods). For example,
struct A {
// getter
int& getValue() const { return value_; }
// setter
void setValue(const int& value) {
// validate value here
value_ = value;
}
private:
int value_;
};
By the way, .NET properties are implemented are methods under the hood.